Obsah
1. Moonscript: jazyk inspirovaný CoffeeScriptem určený pro ekosystém jazyka Lua
3. Příklad podobného transpřekladače pro odlišný ekosystém: Coconut
5. Tradiční začátek – „Hello, world!“ v Moonscriptu i v jazyce Lua
10. Kombinace aritmetické, logické i řetězcové operace s přiřazením
11. Modifikovaná syntaxe volání funkcí
12. Definice nových funkcí operátorem ->
13. Prázdná funkce bez parametrů
14. Funkce s tělem, ovšem bez parametrů
16. Definice funkce s parametry
17. Návrat většího množství hodnot z funkce
18. Parametry funkce s implicitní hodnotou
19. Repositář s demonstračními příklady
1. Moonscript: jazyk inspirovaný CoffeeScriptem určený pro ekosystém jazyka Lua
„The syntax of MoonScript has been heavily inspired by the syntax of CoffeeScript. MoonScript is CoffeeScript for Lua.“
S programovacím jazykem Lua jsme se již na stránkách tohoto serveru setkali, a to dokonce již mnohokrát. Většina článků o tomto jazyku je shrnuta do seriálu Programovací jazyk Lua a taktéž jsme se Luou zabývali ve druhém seriálu nazvaném Torch: framework pro strojové učení. Víme již, že se tento programovací jazyk stal oblíbený například mezi tvůrci her, v nichž je použit pro skriptování. To ovšem není zdaleka vše, protože Lua je použita například v systému LuaTeX, v databázi Redis, ve webovém serveru Nginx a v neposlední řadě i v textovém editoru Neovim. Jednoduše použitelná syntaxe a současně i poměrně velká vyjadřovací schopnost jazyka Lua by však pravděpodobně nedostačovala pro jeho masovější rozšíření, a to zejména v situaci, kdy tuto niku programovacích jazyků do značné míry okupuje Python, kterému se již úspěšně podařilo „odstavit“ některé konkurenty.
Zajímavé je i použití jazyka Lua v oblasti mikrořadičů, kam se tento jazyk (podle mého názoru) hodí lépe, než výše zmíněný Python, resp. jeho varianta nazvaná MicroPython. Ovšem některé syntaktické konstrukce jazyka Lua (a ještě ve větší míře sémantika) by si zasloužily vylepšení. Samotná Lua je velmi stabilním jazykem, který se příliš nevyvíjí, takže tvůrci různých rozšíření jsou postaveni před úkol zachování kompatibility s celým ekosystémem (tedy s interpretrem Lua i s LuaJITem). Podobný problém řeší tvůrci nových jazyků pro webové prohlížeče, protože i zde byl (až do relativně nedávné doby) podporován jediný jazyk – JavaScript. Řešením jsou transpřekladače (transpilery), které zdrojový kód napsaný v jednom jazyku převedou do jazyku jiného, v našich příkladech buď do jazyka Lua nebo do JavaScriptu. A právě takovým způsobem je implementován programovací jazyk Moonscript, který je inspirován CoffeeScriptem (ovšem i tvůrci ES6 se nechali CoffeeScriptem ovlivnit, což ostatně sami potvrzují). Na Moonscript se můžeme dívat jako na jakýsi hybrid mezi Pythonem, jazykem Lua a jazykem Ruby.
Moonscript přináší mnoho zajímavých prvků, například:
- Jednodušší deklaraci lokálních proměnných
- Interpolaci řetězců
- Podporu pro víceřádkové řetězce
- Deklaraci funkcí operátorem „šipky“
- Deklaraci metod operátorem „tlusté šipky“
- Deklarace funkcí s implicitní hodnotou parametrů
- Rozšíření tabulky operátorů
- Prakticky každý příkaz je současně i výrazem vracejícím hodnotu (smyčky, podmínky)
- Generátorová notace seznamu (list comprehensions)
- Generátorová notace tabulky (table comprehensions)
- Vylepšený slicing
- Deklarace tříd
- Deklarace třídních proměnných
- Podpora pro anonymní třídy
- atd.
2. Svět transpřekladačů
Problematikou takzvaných transpřekladačů (anglicky transcompilers nebo taktéž source-to-source compilers) jsme se již na stránkách Rootu zabývali, a to dokonce několikrát. Připomeňme si například projekty ClojureScript (což je transpřekladač Clojure → JavaScript), popř. projekty lua2js (transpřekladač Lua → opět JavaScript) a Wisp (programovací jazyk podobný Clojure transpřekládaný opět do JavaScriptu). V předchozích větách byl použit výraz „transpřekladač“, takže si ho ve stručnosti vysvětlíme. Transpřekladače jsou nástroje sloužící pro překlad algoritmů zapsaných v nějakém zdrojovém programovacím jazyce do zvoleného cílového programovacího jazyka (ovšem nikoli do nativního kódu či bajtkódu, to je totiž role běžných překladačů).
Transpřekladače se v informatice používají již po několik desetiletí; například se stále můžeme setkat s nástroji, které převádí kód z nějakého vyššího programovacího jazyka do Céčka, které je v současnosti s trochou nadsázky chápáno jako „univerzální assembler“. Asi nejznámějším příkladem z této oblasti je nástroj nazvaný web2c, jenž slouží pro transformaci zdrojových kódů TeXu do céčka. Transpřekladače byly – a to zejména v minulém desetiletí, konkrétně před příchodem ES6 – velmi populární i mezi programátory webových aplikací, a to zejména z toho důvodu, že webové prohlížeče nativně podporují většinou pouze JavaScript, který je tak přirozeně cílovým jazykem transpřekladačů (proto se mu také někdy říká „assembler pro web“, viz též odkazy na konci článku). Určitou revolucí byl v této oblasti právě CoffeeScript, ke kterému se dnes ještě několikrát vrátíme. A mnoho vlastností CoffeeScriptu bylo převzato do ES6 (ECMAScript 2015), což způsobilo postupný odklon od CoffeeScriptu.
Z praxe můžeme uvést například následující projekty založené na transpřekladači:
# | Jazyk či transpřekladač | Poznámka |
---|---|---|
1 | CoffeeScript | přidání syntaktického cukru do JavaScriptu, dnes popisovaný Moonscript je jím inspirován |
2 | ClojureScript | překlad aplikací psaných v jazyce Clojure do JavaScriptu |
3 | TypeScript | nadmnožina jazyka JavaScript, přidání datových typů |
4 | 6to5 | transpřeklad z ECMAScript 6 (v roce 2015 nová varianta JavaScriptu) do starší varianty JavaScriptu |
5 | Kaffeine | rozšíření JavaScriptu (opět!) o nové vlastnosti |
6 | RedScript | programovací jazyk inspirovaný Ruby |
7 | GorillaScript | další rozšíření JavaScriptu (JavaScript evidentně potřeboval různá rozšíření :-) |
8 | ghcjs | transpřekladač pro fanoušky programovacího jazyka Haskell |
9 | Haxe | transpřekladač, mezi jehož cílové jazyky patří i Java a JavaScript |
10 | Wisp | transpřekladač jazyka podobného Clojure, opět do JavaScriptu |
11 | ScriptSharp | transpřekladač z C# do JavaScriptu |
12 | Dart | transpřekladač z jazyka Dart do JavaScriptu |
13 | COBOL → C | transpřekladač OpenCOBOL |
14 | COBOL → Java | transpřekladač P3COBOL |
15 | lua2js | transpřekladač jazyka Lua, opět do JavaScriptu |
16 | Coconut | transpřekladač do Pythonu |
17 | Moonscript | transpřekladač do jazyka Lua, jímž se budeme zabývat dnes |
3. Příklad podobného transpřekladače pro odlišný ekosystém: Coconut
Jeden zajímavý a přitom s dnešním tématem velmi dobře korespondující tranpřekladač se jmenuje Coconut. Ten je navržen takovým způsobem, aby byl zpětně kompatibilní s Pythonem. To znamená, že skript napsaný v Pythonu je současně i skriptem napsaným v jazyku Coconut (což je zajímavé, protože do Coconutu byla přidána tři nová klíčová slova data, match a case, jejichž význam se však rozlišuje z kontextu). Coconut je tak možné považovat za sémantické i syntaktické rozšíření Pythonu, přičemž se autor tohoto jazyka zaměřil především na funkcionální rysy (funkce vyššího řádu, neměnitelné hodnoty, podpora pro tvorbu kolon a kompozic funkcí atd.) a taktéž do Coconutu přidal podporu pro pattern matching (inspiraci získal zde). Coconut může pracovat jako interpret s interaktivní smyčkou REPL či jako transpřekladač (transcompiler, transpiler) do jazyka Python, takže je možné použít celý pythonovský ekosystém. Přesnější informace o tom, se kterými verzemi Pythonu je Coconut kompatibilní, naleznete na adrese http://coconut.readthedocs.io/en/master/DOCS.html#compatible-python-versions.
Vzhledem k tomu, že je programovací jazyk Coconut implementován jako transpřekladač a současně se jedná o jazyk tvořící nadmnožinu Pythonu, je v něm možné použít prakticky všechny knihovny pythonovského ekosystému, což platí zejména pro CPython a PyPy, i když větší problémy nelze předpokládat ani v případě použití Jythonu či IronPythonu. Navíc je pro všechny programátory znající Python vlastně velmi jednoduché přejít na Coconut – stále je totiž možné využít stávající syntaxi a sémantiku Pythonu a rozšíření přidaná Coconutem použít jen v těch místech, kde to má v daný okamžik význam. To je zásadní rozdíl od některých jiných (nejenom funkcionálních) jazyků, s nimiž se programátor může setkat a které mnohdy vyžadují, aby se začal učit jak nový programovací jazyk, tak i jeho ekosystém (který je navíc u nově vznikajících jazyků zpočátku malý, navíc může být spousta knihoven dostupných jen v alfa verzích).
Použití transpřekladače však přináší i některé nevýhody, které se projeví například ve chvíli, kdy v programu vznikne chyba či nezachycená výjimka. V takovém případě totiž získáme stack trace platný pro výsledný pythonovský program, nikoli pro uživatelem vytvořený kód. V některých prostředích lze tomuto negativnímu jevu zabránit použitím technologie source map, což jsou (zjednodušeně řečeno) soubory s mapováním mezi řádkem+příkazem v transpilovaném kódu a řádkem+příkazem v originálním zdrojovém kódu (v tomto případě je i minifikace považována za jednu z forem transpilace).
4. Instalace Moonscriptu
Po krátké odbočce do ekosystému programovacího jazyka Python a transpřekladačů do tohoto dnes velmi populárního programovacího jazyka se vraťme k projektu Moonscript. Popíšeme si totiž instalaci Moonscriptu, a to jak s využitím distribuce ULua, tak i s využitím správce balíčků nazvaného LuaRocks.
Pokud máte nainstalován nástroj ULua, je instalace Moonscriptu snadná. Nejdříve se přesvědčíme, že daný balíček vůbec existuje:
$ cd ulua/bin $ ./upkg available |grep moonscript + crescent | crescent : a command-line program for moonscript - with pretty colors! | 0.3.0-103, 0.2.0-103, 0.1.1-103 + moon | moonscript : A programmer friendly language that compiles to Lua | 0.5.0-103, 0.4.0-103, 0.3.2-103 + moongrahams | moongrahams : lua/moonscript library for performing bayesian analysis | 0.0-103 + moonpick | moonpick : An alternative moonscript linter. | 0.8-103, 0.6-103 + moonscript | moonscript : A programmer friendly language that compiles to Lua | 0.5.0-103, 0.4.0-103, 0.3.2-103 + placeholder | moon-watch : better -watch for moonscript(moonc) for MacOS | 0.2-203
Zobrazit si můžeme i podrobnější informace o nalezeném balíčku moonscript:
$ ./upkg available moonscript Module information: name : moonscript version : 0.5.0-103 require : alt_getopt~0.7, lfs~1.5, lpeg~0.10, luajit~2.0, moon~0.5.0-103, re~0.10 description : moonscript : A programmer friendly language that compiles to Lua homepage : http://moonscript.org license : MIT
Ve třetím kroku provedeme instalaci balíčku moonscript i všech dalších potřebných balíčků, což je zejména balíček lpeg s podporou regulárních výrazů:
$ ./upkg add moonscript Installing matching module and its requirements: + alt_getopt | alt-getopt : Process application arguments the same way as getopt_long | 0.8.0-103 + lpeg | lpeg : Parsing Expression Grammars For Lua | 0.12.2-103 + moon | moonscript : A programmer friendly language that compiles to Lua | 0.5.0-103 + moonscript | moonscript : A programmer friendly language that compiles to Lua | 0.5.0-103 + re | lpeg : Parsing Expression Grammars For Lua | 0.12.2-103 Confirm (y/n)? Downloading: + /pkg/alt_getopt/0.8.0-103 | 100% of 4KB + /pkg/moon/0.5.0-103 | 100% of 14KB + /pkg/re/0.12.2-103 | 100% of 2KB + /pkg/lpeg/0.12.2-103 | 100% of 133KB + /pkg/moonscript/0.5.0-103 | 100% of 58KB Done
Po provedení předchozích kroků by se mělo v adresáři ~/ulua/bin/ objevit několik nových spustitelných souborů:
$ ls -1 moon moonc moonc.cmd moon.cmd scilua scilua.cmd upkg upkg.cmd
A obsah adresáře ~/ulua se změní následovně:
. ├── alt_getopt │ └── 0_8_0+103 ├── bin ├── clib_libcurl │ └── 7_42_1+3 ├── clib_libopenblas │ └── 0_2_15 ├── cURL │ └── 0_3_1+103 ├── host │ ├── init │ ├── pkg │ └── tmp ├── lcurl │ └── 0_3_1+103 ├── lfs │ └── 1_6_3+203 ├── lpeg │ └── 0_12_2+103 ├── luajit │ └── 2_1_head20151128 ├── moon │ └── 0_5_0+103 ├── moonscript │ └── 0_5_0+103 ├── pkg │ └── 1_0_beta10 ├── re │ └── 0_12_2+103 ├── sci │ └── 1_0_beta12 ├── sci-lang │ └── 1_0_beta10 ├── serpent │ └── 0_28+103 └── xsys └── 1_0_2
Alternativně je možné pro instalaci použít správce balíčků luarocks, a to následovně:
$ luarocks install moonscript
V obou případech by měl být k dispozici příkaz moon a moonc:
$ ./moon -v MoonScript version 0.5.0 $ ./moon --help Usage: /home/ptisnovs/ulua/bin/../moonscript/0_5_0+103/__bin/moon [options] [script [args]] -h Print this message -d Disable stack trace rewriting -c Collect and print code coverage -v Print version
$ ./moonc --help Usage: /home/ptisnovs/ulua/bin/../moonscript/0_5_0+103/__bin/moonc [options] files... -h Print this message -w Watch file/directory -t path Specify where to place compiled files -o file Write output to file -p Write output to standard out -T Write parse tree instead of code (to stdout) -X Write line rewrite map instead of code (to stdout) -l Perform lint on the file instead of compiling -b Dump parse and compile time (doesn't write output) -v Print version -- Read from standard in, print to standard out (Must be first and only argument)
5. Tradiční začátek – „Hello, world!“ v Moonscriptu i v jazyce Lua
V programovacím jazyce Lua lze program či skript typu „Hello, world!“ napsat následovně:
-- -- Skript zapsaný v jazyce Lua -- print("Hello, world!")
Ve skutečnosti je možné při volání funkce mající pouze jediný parametr typu řetězec, popř. tabulka použít při volání funkce zápis bez kulatých závorek, tedy takto:
-- -- Skript zapsaný v jazyce Lua -- print "Hello, world!"
Přesně takovým způsobem se zapisuje volání funkce i v Moonscriptu, a to i pro funkce s větším počtem parametrů, popř. parametrů jiného typu než jen řetězec a tabulka:
-- -- Skript zapsaný v jazyce Moonscript -- print "Hello, world!"
Překlad předchozího skriptu do jazyka Lua nástrojem moonc by měl vytvořit tento zdrojový soubor plně kompatibilní s jazykem Lua:
-- -- Skript transpilovaný do jazyka Lua -- return print("Hello, world!")
6. Lokální proměnné
Jedním z méně šťastných rozhodnutí při návrhu programovacího jazyka Lua bylo, že (automaticky) nově vytvářené proměnné jsou globální, nikoli lokální (jak je tomu například v Pythonu). To například znamená, že následující skript vypíše hodnotu 42, protože proměnná x byla ve funkci definována jako globální:
function foo() x = 42 end foo() print(x)
V Moonscriptu se před (automaticky) vytvářené proměnné po transpřekladu přidává klíčové slovo local, takže proměnná je vytvořena jako lokální v rámci aktuálního bloku (typicky funkce):
-- -- Skript zapsaný v jazyce Moonscript -- message = "Hello, world!" print message
Výsledek transpřekladu:
-- -- Skript transpilovaný do jazyka Lua -- local message = "Hello, world!" return print(message)
Samozřejmě ovšem není klíčové slovo local přidáváno před každý přiřazovací výraz, ale pouze při prvním přiřazení:
-- -- Skript zapsaný v jazyce Moonscript -- message = nil message = "Hello, world!" print message
Výsledek transpřekladu tohoto demonstračního skriptu:
-- -- Skript transpilovaný do jazyka Lua -- local message = nil message = "Hello, world!" return print(message)
V případě, že se neprovádí přiřazení, ale pouze čtení proměnné, vrátí se v obou jazycích hodnota nil:
-- -- Skript zapsaný v jazyce Moonscript -- print message
Výsledek:
-- -- Skript transpilovaný do jazyka Lua -- return print(message)
7. Interpolace řetězců
Velmi užitečným rozšířením CoffeeScriptu a jím inspirovaného Moonscriptu je podpora pro takzvanou interpolaci řetězců. Jedná se o mechanismus, který vývojářům umožňuje psát do řetězce výrazy, které jsou vyhodnoceny, převedeny na řetězec a vloženy do generovaného řetězce. Tyto výrazy se zapisují mezi znaky #{ a }. Alternativní zápis klasického programu typu „Hello, world!“ založený na interpolaci řetězců tedy může vypadat například takto:
-- -- Skript zapsaný v jazyce Moonscript -- a = "Hello" b = "world" print "#{a}, #{b}!"
Programovací jazyk Lua interpolaci řetězců neumožňuje a proto bylo nutné předchozí skript transpřeložit tímto způsobem:
-- -- Skript transpilovaný do jazyka Lua -- local a = "Hello" local b = "world" return print(tostring(a) .. ", " .. tostring(b) .. "!")
Mezi znaky #{ a } může být zapsán i složitější výraz:
-- -- Skript zapsaný v jazyce Moonscript -- x = 6 y = 7 print "#{x} * #{y} = #{x*y}"
Transformace do jazyka Lua:
-- -- Skript transpilovaný do jazyka Lua -- local x = 6 local y = 7 return print(tostring(x) .. " * " .. tostring(y) .. " = " .. tostring(x * y))
Interpolaci řetězců lze snadno zakázat, a to konkrétně použitím apostrofů namísto uvozovek při zápisu řetězce:
-- -- Skript zapsaný v jazyce Moonscript -- x = 6 y = 7 print '#{x} * #{y} = #{x*y}'
Transformace do jazyka Lua nyní interpolaci řetězců nepoužívá:
-- -- Skript transpilovaný do jazyka Lua -- local x = 6 local y = 7 return print('#{x} * #{y} = #{x*y}')
8. Víceřádkové řetězce
V Moonscriptu jsou, ostatně naprosto stejně jako v CoffeeScriptu, podporovány víceřádkové řetězce. Ty se přitom zapisují tím nejjednodušším možným způsobem – pokud řetězec na řádku nekončí uvozovkami (nebo apostrofem), jsou odpovídající koncové uvozovky hledány na dalších řádcích. Není tedy nutné mít speciální syntaktická pravidla pro jednořádkové a víceřádkové řetězce. Podívejme se nyní na jednoduchý demonstrační příklad s běžným testovacím „dokumentem“ Lorem ipsum:
-- -- Skript zapsaný v jazyce Moonscript -- message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." print message
Tento skript se převede do jazyka Lua takovým způsobem, že se řetězec zapsaný na více řádků převede na jednořádkový řetězec, v němž se ovšem explicitně použijí řídicí znaky \n (což je pochopitelně mnohem méně čitelné, než původní skript):
-- -- Skript transpilovaný do jazyka Lua -- local message = "Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit, sed do\neiusmod tempor incididunt ut labore et\ndolore magna aliqua. Ut enim ad minim\nveniam, quis nostrud exercitation ullamco\nlaboris nisi ut aliquip ex ea commodo\nconsequat." return print(message)
9. Nové operátory
Původní programovací jazyk Lua nabízí programátorům následující sadu operátorů, které se používají při zápisu aritmetických výrazů, logických výrazů, relačních výrazů, popř. pro práci s řetězci:
Priorita | Kategorie | Operátor | Asociativita |
---|---|---|---|
1 | unární | not # – | zprava doleva |
2 | konkatenace | .. | zprava doleva |
3 | multiplikativní | * / % | zleva doprava |
4 | aditivní | + – | zleva doprava |
5 | relační | < > <= > | zleva doprava |
6 | test na rovnost/nerovnost | == ~= | zleva doprava |
7 | logický součin | and | zleva doprava |
I | logický součet | or | zleva doprava |
V jazyce Moonscript je počet operátorů zvýšen. Prvním (staro)novým operátorem je operátor !=, který má stejný význam jako standardní operátor ~= – jedná se tedy o test na nerovnost dvou hodnot. Důvodem pro přidání operátoru != je pochopitelně lepší srozumitelnost pro programátory znalé některého jazyka z céčkové vývojové větve programovacích jazyků.
Funkci nového operátoru si můžeme snadno ověřit například na tomto skriptu:
-- -- Skript zapsaný v jazyce Moonscript -- x = 1 y = 2 print x==2 print x!=2 print x~=2
Kód transpilovaný do jazyka Lua ukazuje, že != je skutečně převeden na operátor ~=:
-- -- Skript transpilovaný do jazyka Lua -- local x = 1 local y = 2 print(x == 2) print(x ~= 2) return print(x ~= 2)
10. Kombinace aritmetické, logické i řetězcové operace s přiřazením
S operátorem přiřazení, který se zapisuje jediným znakem =, jsme se již setkali, takže jen pro úplnost:
-- -- Skript zapsaný v jazyce Moonscript -- a = 10 b = "foo" c = {1,2,3} print a print b print c
Korespondující kód v jazyku Lua:
-- -- Skript transpilovaný do jazyka Lua -- local a = 10 local b = "foo" local c = { 1, 2, 3 } print(a) print(b) return print(c)
Tento operátor byl rozšířen o možnost provést přiřazení společně s další aritmetickou, logickou či řetězcovou operací, a to za předpokladu, že je použita stejná proměnná jak na levé straně, tak i na pravé straně původního výrazu. To znamená, že výraz:
x = x ⊕ y
lze zapsat i formou:
x ⊕= y
a to pro jakýkoli binární operátor zmíněný v předchozí kapitole.
Podívejme se nyní na kombinaci přiřazení s aritmetickou operací:
-- -- Skript zapsaný v jazyce Moonscript -- a = 10 b = 20 c = 30 a += 10 b /= 2 c %= 7 print a print b print c
Převod do jazyka Lua:
-- -- Skript transpilovaný do jazyka Lua -- local a = 10 local b = 20 local c = 30 a = a + 10 b = b / 2 c = c % 7 print(a) print(b) return print(c)
Přiřazení lze zkombinovat i s operací pro konkatenaci řetězců:
-- -- Skript zapsaný v jazyce Moonscript -- msg = "Hello, " msg ..= "world" msg ..= "!" print msg
Převod do jazyka Lua:
-- -- Skript transpilovaný do jazyka Lua -- local msg = "Hello, " msg = msg .. "world" msg = msg .. "!" return print(msg)
A konečně je možné přiřazení zkombinovat s logickými operátory:
-- -- Skript zapsaný v jazyce Moonscript -- x = true y = false print x print y x and= y y or= true print x print y
Převod do jazyka Lua:
-- -- Skript transpilovaný do jazyka Lua -- local x = true local y = false print(x) print(y) x = x and y y = y or true print(x) return print(y)
11. Modifikovaná syntaxe volání funkcí
V Moonscriptu došlo i k modifikaci (resp. přesněji řečeno k rozšíření) syntaxe při volání funkcí. Nejprve se podívejme na skript, který je sice psaný v Moonscriptu, ale je současně kompatibilní i s Luou. Povšimněte si, že u funkce s jediným parametrem typu řetězec není nutné okolo tohoto parametru psát kulaté závorky:
-- -- Skript zapsaný v jazyce Moonscript -- print "Hello" print() print("world") print("foo", "bar")
Výsledek transpřekladu:
-- -- Skript transpilovaný do jazyka Lua -- print("Hello") print() print("world") return print("foo", "bar")
V Moonscriptu jsou ve skutečnosti závorky okolo parametrů volitelné a není je nutné používat. Má to ovšem jeden háček – jak zajistit volání funkce bez parametrů. To je řešeno přidáním znaku ! za jméno funkce (ovšem pokud vám to připadne divné, je pochopitelně stále možné použít prázdné kulaté závorky):
-- -- Skript zapsaný v jazyce Moonscript -- print "Hello" print! print "world" print "foo", "bar"
Výsledek transpřekladu:
-- -- Skript transpilovaný do jazyka Lua -- print("Hello") print() print("world") return print("foo", "bar")
12. Definice nových funkcí operátorem ->
V CoffeeScriptu se (alespoň v doméně mainstreamových programovacích jazyků) poprvé objevil „operátor šipky“ použitý pro definici nových anonymních funkcí, pochopitelně s tím, že výslednou funkci je možné přiřadit symbolu a tím ji udělat neanonymní (pojmenovanou). Kromě operátoru „tenké“ šipky -> existuje ještě operátor „tlusté“ šipky =>, který má odlišný význam, protože mj. zajišťuje navázání na v daném kontextu aktuální hodnotu this, resp. self. Později se tento operátor (po určité modifikaci sémantiky, ke které se ještě vrátíme) objevil i v ES6, takže je dnes široce používán.
Vraťme se však k operátoru „tenké“ šipky, kterým lze v Moonscriptu nahradit klasickou definici funkce s využitím klíčových slov function, end a return. Tento operátor byl převzat z CoffeeScriptu, kde měl následující podobu:
-- -- Skript zapsaný v jazyce CoffeeScript -- square = (x) -> x * x cube = (x) -> square(x) * x
Transpřeklad do JavaScriptu vypadá následovně:
-- -- Skript transpilovaný do jazyka JavaScript -- var cube, square; square = function(x) { return x * x; }; cube = function(x) { return square(x) * x; };
13. Prázdná funkce bez parametrů
Začneme poněkud umělým příkladem, a to konkrétně funkcí bez parametrů, která navíc ani nemá žádné tělo. V Moonscriptu se tato funkce deklaruje a volá takto:
-- -- Skript zapsaný v jazyce Moonscript -- noop_function = -> noop_function!
Výsledkem transpřekladu je skutečně definice nové funkce, která má prázdné tělo, tedy: function() end:
-- -- Skript transpilovaný do jazyka Lua -- local noop_function noop_function = function() end return noop_function()
14. Funkce s tělem, ovšem bez parametrů
Druhou funkcí vytvořenou operátorem šipky, kterou si dnes ukážeme, je funkce, která má tělo, ovšem neakceptuje žádné parametry. V případě, že tělo této funkce obsahuje pouze jediný příkaz, je ji možné zapsat (a následně zavolat) následovně:
-- -- Skript zapsaný v jazyce Moonscript -- noarg_function = -> print "Hello, world!" noarg_function!
Povšimněte si, že při transpřekladu do jazyka Lua bude poslední (a současně i jediný) příkaz v těle funkce zavolán uvnitř konstrukce return, což je pro Moonscript typické:
-- -- Skript transpilovaný do jazyka Lua -- local noarg_function noarg_function = function() return print("Hello, world!") end return noarg_function()
Funkce obsahující více příkazů ve svém těle se typicky zapisuje na větší počet řádků:
-- -- Skript zapsaný v jazyce Moonscript -- noarg_function = -> message = "Hello, " message ..= "world!" print message noarg_function!
Ze skriptu transformovaného do jazyka Lua je patrné, že poslední příkaz v těle funkce je opět zavolán v rámci konstrukce return a tudíž je jeho návratová hodnota návratovou hodnotou celé funkce:
-- -- Skript transpilovaný do jazyka Lua -- local noarg_function noarg_function = function() local message = "Hello, " message = message .. "world!" return print(message) end return noarg_function()
15. Návratová hodnota funkce
Předchozí funkce vracely návratovou hodnotu volané funkce print, což neukazuje všechny možnosti, které nám programovací jazyk Moonscript nabízí. Ukažme si tedy způsob definice funkce, která vrací skutečnou (vypočtenou) hodnotu. Prozatím nevíme, jak se deklaruje funkce s parametry, takže použijeme funkci bez parametrů, ovšem s návratovou hodnotou. Zápis využívající explicitní konstrukci return bude vypadat takto:
-- -- Skript zapsaný v jazyce Moonscript -- my_random = -> return math.random(1, 10) for i = 0, 10 do print my_random!
Překlad do jazyka Lua:
-- -- Skript transpilovaný do jazyka Lua -- local my_random my_random = function() return math.random(1, 10) end for i = 0, 10 do print(my_random()) end
Ovšem konstrukci return je ve skutečnosti možné vynechat, protože hodnota posledního výrazu ve funkci je z této funkce vrácena automaticky:
-- -- Skript zapsaný v jazyce Moonscript -- my_random = -> math.random(1, 10) for i = 0, 10 do print my_random!
Překlad do jazyka Lua je v tomto případě totožný s předchozím příkladem:
-- -- Skript transpilovaný do jazyka Lua -- local my_random my_random = function() return math.random(1, 10) end for i = 0, 10 do print(my_random()) end
16. Definice funkce s parametry
Nyní se podívejme na způsob zápisu funkce s parametry. Jména těchto parametrů je nutné zapsat do kulatých závorek a uvést je před operátor šipky. Jednoduchá funkce vracející součin svých dvou parametrů tedy může vypadat takto:
-- -- Skript zapsaný v jazyce Moonscript -- multiply = (x, y) -> x * y print multiply 6,7
Takto nadefinovaná funkce je transpilována na příslušný Lua skript následujícím způsobem:
-- -- Skript transpilovaný do jazyka Lua -- local multiply multiply = function(x, y) return x * y end return print(multiply(6, 7))
17. Návrat většího množství hodnot z funkce
V případě, že je nutné z funkce vrátit větší množství hodnot (což dovoluje již programovací jazyk Lua), lze použít následující styl zápisu:
-- -- Skript zapsaný v jazyce Moonscript -- swap = (x, y) -> y, x print swap "foo", "bar"
V tomto případě je výsledek posledního (a jediného) výrazu ve funkci vrácen jako dvojice parametrů, což si můžeme snadno ověřit spuštěním transpilovaného kódu, který by měl vypsat předané parametry, ovšem v opačném pořadí:
-- -- Skript transpilovaný do jazyka Lua -- local swap swap = function(x, y) return y, x end return print(swap("foo", "bar"))
18. Parametry funkce s implicitní hodnotou
Poněkud složitější je práce transpřekladače Moonscriptu ve chvíli, kdy budeme potřebovat deklarovat funkci, jejích (některé) parametry budou mít výchozí hodnotu. To je jazyková konstrukce, kterou jazyk Lua nepodporuje, ovšem je to v praxi velmi užitečná konstrukce. Proto ji můžeme použít v Moonscriptu. Způsob zápisu odpovídá (například) programovacímu jazyku Python:
-- -- Skript zapsaný v jazyce Moonscript -- sum = (x=0, y=0, z=0) -> x + y + z print sum! print sum 1 print sum 1, 2 print sum 1, 2, 3
Vidíme, že funkci sum lze volat bez parametrů, s jedním parametrem, se dvěma parametry nebo s parametry třemi. Transpřeklad do jazyka Lua bude vypadat takto:
-- -- Skript transpilovaný do jazyka Lua -- local sum sum = function(x, y, z) if x == nil then x = 0 end if y == nil then y = 0 end if z == nil then z = 0 end return x + y + z end print(sum()) print(sum(1)) print(sum(1, 2)) return print(sum(1, 2, 3))
Vidíme, že parametry jsou vyhodnocovány v těle funkce! To nám umožňuje zajímavý trik – výchozí hodnoty některých parametrů mohou být odvozeny z jiných výchozích parametrů, protože víme, že výpočet skutečných hodnot parametrů se provádí zleva doprava:
-- -- Skript zapsaný v jazyce Moonscript -- weird_sum = (x=0, y=x+1, z=y*2) -> x + y + z print sum! print sum 1 print sum 1, 2 print sum 1, 2, 3
Výsledkem je korektní kód reprezentovaný v jazyce Lua:
-- -- Skript transpilovaný do jazyka Lua -- local weird_sum weird_sum = function(x, y, z) if x == nil then x = 0 end if y == nil then y = x + 1 end if z == nil then z = y * 2 end return x + y + z end print(sum()) print(sum(1)) print(sum(1, 2)) return print(sum(1, 2, 3))
19. Repositář s demonstračními příklady
Všechny dnes popsané demonstrační příklady určené pro poslední stabilní verzi transpřekladače Moonscript byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/moonscript-examples. Tyto demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes prozatím malý) repositář:
20. Odkazy na Internetu
- Stránky projektu Moonscript
https://moonscript.org/ - Moonscript na GitHubu
https://github.com/leafo/moonscript - MoonScript online compiler
https://moonscript.org/compiler/ - Vydání Moonscriptu
https://github.com/leafo/moonscript/releases - Moonscript-vim
https://github.com/leafo/moonscript-vim - Moonscript Examples
https://github.com/leafo/moonscript/wiki/Moonscript-Examples - CoffeeScript
https://coffeescript.org/ - CoffeeScript na Wikipedii
https://en.wikipedia.org/wiki/CoffeeScript - CoffeeScript: řádně oslazený JavaScript
https://zdrojak.cz/clanky/coffeescript-radne-oslazeny-javascript/ - CoffeeScript: druhá dávka steroidů pro vaše skripty
https://zdrojak.cz/clanky/coffeescript-druha-davka-steroidu-pro-vase-skripty/ - Why CoffeeScript is still alive
https://codeburst.io/why-coffeescript-is-still-alive-aeb369b91b85 - The CoffeeScript Wiki
https://github.com/jashkenas/coffeescript/wiki - CoffeeScript In The Wild
https://github.com/jashkenas/coffeescript/wiki/In-The-Wild - How CoffeeScript Got Forgotten
https://betterprogramming.pub/how-coffeescript-got-forgotten-812328225987 - ULua: Universal Lua Distribution
https://ulua.io/index.html - LuaRocks
https://luarocks.org/ - Awesome Lua – A curated list of quality Lua packages and resources.
https://github.com/LewisJEllis/awesome-lua - LuaJIT
https://luajit.org/ - Running LuaJIT
https://luajit.org/running.html - LuaJIT na GitHubu
https://github.com/luajit - Lua Implementations
http://lua-users.org/wiki/LuaImplementations - Coconut: funkcionální jazyk s pattern matchingem kompatibilní s Pythonem
https://www.root.cz/clanky/coconut-funkcionalni-jazyk-s-pattern-matchingem-kompatibilni-s-pythonem/ - Coconut aneb funkcionální nadstavba nad Pythonem (2.část)
https://www.root.cz/clanky/coconut-aneb-funkcionalni-nadstavba-nad-pythonem-2-cast/ - Coconut: Simple, elegant, Pythonic functional programming
http://coconut-lang.org/ - coconut 1.1.0 (Python package index)
https://pypi.python.org/pypi/coconut/1.1.0 - Coconut Tutorial
http://coconut.readthedocs.io/en/master/HELP.html - Coconut FAQ
http://coconut.readthedocs.io/en/master/FAQ.html - Coconut Documentation
http://coconut.readthedocs.io/en/master/DOCS.html - Coconut na Redditu
https://www.reddit.com/r/Python/comments/4owzu7/coconut_functional_programming_in_python/ - Repositář na GitHubu
https://github.com/evhub/coconut - patterns
https://github.com/Suor/patterns - Source-to-source compiler
https://en.wikipedia.org/wiki/Source-to-source_compiler - The Lua VM, on the Web
https://kripken.github.io/lua.vm.js/lua.vm.js.html - Lua.vm.js REPL
https://kripken.github.io/lua.vm.js/repl.html - lua2js
https://www.npmjs.com/package/lua2js - Wisp na GitHubu
https://github.com/Gozala/wisp - Wisp playground
http://www.jeditoolkit.com/try-wisp/ - REPL v prohlížeči
http://www.jeditoolkit.com/interactivate-wisp/ - Minification (programming)
https://en.wikipedia.org/wiki/Minification_(programming) - Roblox
https://en.wikipedia.org/wiki/Roblox - Category:Lua (programming language)-scriptable game engines
https://en.wikipedia.org/wiki/Category:Lua_(programming_language)-scriptable_game_engines - Goodbye Lua (shrnutí následujícího článku)
https://www.reddit.com/r/lua/comments/4ld6ao/goodbye_lua/ - Goodbye, Lua
https://realmensch.org/2016/05/28/goodbye-lua/ - 6th Edition – ECMAScript 2015
https://en.wikipedia.org/wiki/ECMAScript#6th_Edition_%E2%80%93_ECMAScript_2015