Obsah
1. Role maker v lispovských programovacích jazycích
3. Vytvoření makra v Common Lispu
4. Výpis expandovaného makra, řešení dvojího vyhodnocení parametrů
5. Typické Common Lispovské makro „loop“: operace nad seznamy
6. Další příklady použití knihovního makra „loop“: počítané smyčky
7. Základy práce se systémem maker v programovacím jazyku Clojure
8. První písmeno ve zkratce REPL: objekt Reader a jeho makra
9. Makra „quote“ a „syntax-quote“
10. Makra „unquote“ a „unquote-splicing“
11. Makra v programovacím jazyku Racket
12. Vytvoření makra s využitím define-syntax-rule
13. Syntaktické objekty, použití syntax->datum
14. Makro pro prohození obsahu dvou proměnných
17. Původní (nehygienická) makra v Racketu
18. Repositář s demonstračními příklady
1. Role maker v lispovských programovacích jazycích
Jednou z nejzajímavějších vlastností většiny klasických lispovských jazyků je možnost tvorby maker. Vzhledem k tomu, že aplikace naprogramované v LISPu (například v Common Lispu), Scheme či v některém dalším lispovském jazyce (Clojure) jsou tvořeny, stejně jako data, s využitím rekurzivně vnořených seznamů, jsou makra v LISPu založena na manipulaci se seznamy tvořícími program, což je velký rozdíl například oproti makrům implementovaným v céčkovém preprocesoru, kde se jedná o poměrně jednoduché textové záměny (ostatně samotný preprocesor je většinou zcela oddělený od překladače). Vzhledem k tomu, že lispovská makra dokážou manipulovat s vlastním programem (resp. s jeho uloženou podobou), je možné pomocí nich vytvářet například úplně nové jazykové konstrukce (různé smyčky, podmíněné příkazy, částečně vyhodnocované formy atd.) s vlastní syntaxí, což je poměrně unikátní vlastnost, kterou u většiny dalších programovacích jazyků nenajdeme.
- Práce s makry v programovacím jazyku Rust
https://www.root.cz/clanky/prace-s-makry-v-programovacim-jazyku-rust/ - Programovací jazyk Julia: metaprogramování, makra a AST
https://www.root.cz/clanky/programovaci-jazyk-julia-metaprogramovani-makra-a-ast/
Způsob definice maker se v některých ohledech podobá definici funkcí, ale mezi funkcemi a makry existuje jeden zásadní rozdíl.
Funkce vytvořené v LISPu/Scheme/Clojure získávají jako svoje argumenty hodnoty, tj. většinou atomy, (anonymní) funkce nebo seznamy, a vrací taktéž nějakou hodnotu – opět se může jednat o atom, (anonymní) funkci nebo seznam. Funkce jsou vyhodnocovány (volány) až při spuštění programu a jejich argumenty jsou vyhodnocovány dříve, než se funkce zavolá. Makra ovšem jako svůj vstup získávají lispovský kód (zapsaný formou rekurzivně zanořeného seznamu) a vrací taktéž lispovský kód, což nepředstavuje oproti funkcím žádný zásadnější rozdíl. Ovšem na rozdíl od funkcí jsou makra volána již při prvotním zpracovávání programu, podobně jako jsou céčková makra zpracovávána céčkovým preprocesorem (cpp) ještě před vlastní kompilací. Teprve výsledek volání makra (nazývaný taktéž expanze makra) je považován za zápis výrazu, který může být dále zpracován, tj. buď vyhodnocen (interpretační varianty LISPu) nebo zkompilován (varianty LISPu vybavené překladačem). Poznamenejme ještě, že v těle makra se může vyskytovat volání dalšího makra, což znamená, že LISP musí při expanzi maker použít rekurzi (tuto rekurzi lze v případě potřeby zakázat).
Rozdíly mezi makry a funkcemi můžeme ve stručnosti shrnout takto:
Funkce | Makro |
---|---|
volána v runtime | voláno (expandováno) v compile/eval time |
na vstupu akceptuje libovolné hodnoty | akceptuje „syntaktické objekty“ |
na výstupu může vracet libovolnou hodnotu | výstupem je opět syntaktický objekt (expandované makro) |
používá se pro implementaci knihoven i vlastních algoritmů | používá se pro rozšíření a úpravu sémantiky (DSL) |
2. Makra v Common Lispu
Nejprve si ve stručnosti ukažme klasický makrosystém použitý v Common Lispu. Při zápisu maker se poměrně často používají znaky ` (zpětný apostrof), , (čárka) a @ (zavináč). Zpětný apostrof se zapisuje před seznam, kde má podobný význam jako běžný apostrof, který, jak již víme, zabraňuje tomu, aby byl seznam vyhodnocen (jedná se o zkrácený zápis speciální formy quote). V případě zpětného apostrofu je taktéž zabráněno vyhodnocení seznamu, který se za apostrofem nachází, ovšem navíc lze uvnitř takového seznamu použít symbol čárka, který označuje ty položky seznamu, které se naopak vyhodnotit mají – díky tomu je možné přesně řídit, jak se má seznam vyhodnotit, a to až na úroveň jeho jednotlivých prvků. Symbol zavináče zapsaný (společně s čárkou) před nějakou položku seznamu taktéž vede k vyhodnocení této položky, ale odlišným způsobem – položka (kterou může být například další seznam) se přímo vloží do seznamu uvozeného zpětným apostrofem.
Zní to celé složitě? Nejlepší bude, když si způsob použití všech tří symbolů ukážeme na jednoduchých příkladech:
; vytvoříme si dvě proměnné x a y obsahující hodnoty 1 a 2 (setq x 1) 1 (setq y 2) 2 ; pokus o vyhodnocení seznamu (x y) musí skončit chybou, protože ; x není jméno žádné funkce ale proměnné obsahující atom (x y) *** - EVAL: undefined function X ; použití normálního apostrofu zabraňuje vyhodnocení seznamu ; (speciální forma quote) '(x y) (X Y) ; použití zpětného apostrofu taktéž zabraňuje vyhodnocení seznamu `(x y) (X Y) ; zabráníme vyhodnocení celého seznamu, ale naopak si vynutíme vyhodnocení y ; (y se tedy nahradí svou hodnotou) `(x ,y) (X 2) ; zabráníme vyhodnocení celého seznamu, ale naopak si vynutíme vyhodnocení x ; (x se tedy nahradí svou hodnotou) `(,x y) (1 Y) ; seznam jako celek vyhodnocen není (ty by vedlo k chybě - viz výše) ; ale jsou vyhodnoceny obě proměnné x a y `(,x ,y) (1 2)
Vidíme tedy, že vyhodnocování seznamu a jeho položek můžeme velmi přesně řídit a obejít tak standardní pravidla. Použití zavináče je ovšem již poněkud komplikovanější:
; proměnná z obsahuje seznam se třemi symboly (setq z '(www root cz)) (WWW ROOT CZ) ; zabráníme vyhodnocení seznamu obsahujícího z (tedy další seznam) `(1 2 3 z 4 5 6) (1 2 3 Z 4 5 6) ; seznam jako celek není vyhodnocen, ale proměnná z ano ; (před prvkem z je čárka, celý seznam je uvozen zpětným apostrofem) `(1 2 3 ,z 4 5 6) (1 2 3 (WWW ROOT CZ) 4 5 6) ; podobné předchozímu výrazu, ovšem s tím rozdílem, že je seznam uložený ; v proměnné z "rozpuštěn" (zmizí závorky okolo trojice symbolů ; www root cz) `(1 2 3 ,@z 4 5 6) (1 2 3 WWW ROOT CZ 4 5 6)
3. Vytvoření makra v Common Lispu
Nyní si konečně můžeme nějaké jednoduché makro ukázat. Namísto formy defun se v Common Lispu při tvorbě maker používá speciální forma pojmenovaná defmacro. Můžeme se tedy pokusit vytvořit makro, které nahradí svůj parametr výrazem, jenž vypočítá druhou mocninu tohoto parametru:
(defmacro Square (x) `(* ,x ,x))
Toto makro funguje následujícím způsobem: při prvotním načítání LISPovských výrazů (forem) se jakýkoli výskyt (Square cokoli) nahradí formou (* cokoli cokoli) (zde je pěkně vidět, z jakého důvodu existují symboly ` a ,). Teprve tato forma je předána interpretru nebo překladači pro další zpracování, například přímému vyhodnocení. Když je LISPovský program překládán, tak se přeloží až výsledek aplikace makra, což je opět shodné se způsobem překladu céčkového programu.
Makro si můžeme ihned vyzkoušet:
(Square 42) 1764 (Square (+ 1 2)) 9
#define Square(x) ((x)*(x))
Kvůli sémanticky omezenému makrosystému C je nutné symbol x správně uzávorkovat.
4. Výpis expandovaného makra, řešení dvojího vyhodnocení parametrů
Při tvorbě maker se může (a to velmi snadno a často :-) stát, že makro kvůli nějaké chybě neprovádí přesně tu činnost, jakou programátor zamýšlel. Zatímco funkce jsou volány až v době běhu programu, tj. je možné do jejich těla vkládat různé ladicí příkazy (výpis hodnot, logování, aserce) či dokonce běh programu trasovat, u maker to není možné, protože jsou expandována již při překladu programu.
Ovšem v Common Lispu je možné si pomocí zabudované funkce macroexpand nebo macroexpand-1 zobrazit výpis makra po expanzi, což může být velmi užitečné (podobně je možné si nechat zobrazit výsledek činnosti preprocesoru programovacího jazyka C). Zatímco funkce macroexpand zobrazí plně expandované makro (tj. výsledný LISPovský kód po expanzi všech maker, a to i těch vnořených), je po zavolání makra macroexpand-1 zobrazeno makro pouze po první expanzi, což znamená, že uvnitř expandovaného kódu se mohou vyskytovat i volání dalších maker. V praxi se při ladění maker častěji používá právě funkce macroexpand-1, jejíž použití je velmi jednoduché, což si ostatně můžeme snadno ověřit:
; nejprve znovu vytvoříme makro nazvané Square (defmacro Square (x) `(* ,x ,x)) ; expanze makra při volání funkce Square s parametrem jenž je atomem (macroexpand-1 '(Square 42)) (* 42 42) ; ; expanze makra při volání funkce Square s parametrem jenž je formou (macroexpand-1 '(Square (+ 1 2))) (* (+ 1 2) (+ 1 2)) ; ; makro se expanduje i v případě, že se použije neznámý symbol "plus" (macroexpand-1 '(Square (plus 1 2))) (* (PLUS 1 2) (PLUS 1 2)) ;
Až při pohledu na expandované makro nás může napadnout, že vlastně nepracuje dokonale, v některých případech dokonce pracuje chybně. Problém spočívá v tom, že se předaný parametr po expanzi makra vyhodnocuje dvakrát, což samozřejmě není větší problém v případě, že se předává/vyhodnocuje atomická hodnota (číslo) nebo jednoduchý výraz, ovšem kdyby se jednalo například o funkci načítající hodnotu z databáze nebo ze souboru (popř. ze standardního vstupu), bylo by toto načítání prováděno dvakrát a ne jedenkrát, jak by každý programátor při pohledu na program volající makro očekával. Můžeme si to ostatně vyzkoušet (funkce read provádí načtení výrazu ze standardního vstupu):
(Square (read)) 2 ; zadáno uživatelem z klávesnice 3 ; zadáno (druhá a jiná! hodnota) taktéž z klávesnice 6 ; výsledek volání makra = 2*3
Makro se můžeme pokusit „opravit“ například tak, že se použije pomocná lokální proměnná temp:
(defmacro Square2 (x) `(let ((temp ,x)) (* temp temp))) ; podíváme se na expanzi při jeho volání ; s parametrem (+ 1 2) (macroexpand-1 '(Square2 (+ 1 2))) (LET ((TEMP (+ 1 2))) (* TEMP TEMP)) ; ; vidíme, že se výraz (+ 1 2) vyhodnocuje pouze jedenkrát ; otestujeme volání makra při předání funkce read (Square2 (read)) 2 ; zadáno uživatelem z klávesnice 4 ; správný výsledek a navíc program vyžadoval pouze jediný vstup z klávesnice
Pomocí macroexpand-1 je možné prozkoumat i makra dostupná v základní knihovně jazyka:
; makra or a and jsou implementovány tak, aby podporovaly ; zkrácené vyhodnocení logických výrazů (macroexpand-1 '(or foo bar)) (COND (FOO) (T BAR)) ; (macroexpand-1 '(and (foo t) (foo nil))) (COND ((NOT (FOO T)) NIL) (T (FOO NIL))) ;
5. Typické Common Lispovské makro „loop“: operace nad seznamy
Jedním z nejužitečnějších maker, které se nachází ve standardní knihovně Common Lispu, je makro nazvané prozaicky loop, jenž je doplněné o několik dalších pomocných maker a symbolů (ty jsou použity pro doplnění syntaxe o další „klíčová slova“).
S využitím makra loop lze v LISPu (tj. jazyku založeném částečně na funkcionálním paradigmatu, který původně vedl uživatele k používání rekurze namísto psaní programových smyček) realizovat značné množství různých typů programových smyček, například smyčky počítané (s možností změny kroku, o který se změní hodnota čítače či čítačů smyček při každé iteraci), smyčky s podmínkou (odpovídá například Pascalovským smyčkám typu while-do a repeat-until) či iterační smyčky, která v každé iteraci operuje nad prvky seznamů, polí či asociativních polí (for-each). Na příkladu makra loop je pěkně a názorně ukázána jedna z největších výhod LISPovských maker – právě pomocí maker a některých dalších vlastností LISPu je možné v případě potřeby vytvořit zcela nový jazyk s vlastní syntaxí (doménově specifický jazyk).
Na následujících demonstračních příkladech si ukážeme některé možnosti, které makro loop programátorům nabízí:
Smyčka, která postupně iteruje nad všemi elementy uloženými v seznamu (tj. postupně všemi prvky prochází):
(loop for i in '(a b c d) do (print i)) A B C D
Pokud se ve zpracovávaném seznamu nachází další rekurzivně vnořené seznamy, jsou při iterování seznamem chápány jako jeden prvek:
(loop for i in '(a (b c) d) do (print i)) A (B C) D
Programová smyčka, která postupně iteruje (prochází) přes CDR zvoleného seznamu. V první iteraci je do proměnné i přiřazen celý seznam s, ve druhé iteraci (cdr s), ve třetí iteraci (cdr (cdr s)) atd.:
(loop for i on '(a b c d) do (print i)) (A B C D) (A B C) (B C) (C)
Zpracování seznamu obsahujícího ve druhém prvku podseznam:
(loop for i on '(a (b c) d) do (print i)) (A (B C) D) ((B C) D) (D)
Iterace nad dvojicí seznamů a postupná konstrukce seznamu obsahujícího dvojice prvků ze seznamu prvního a druhého. Výsledek není v tomto případě tisknut, ale je vrácen jako návratová hodnota smyčky, tj. lze ho například přiřadit proměnné:
(loop for x in '(a b c d e) for y in '(1 2 3 4 5) collect (list x y)) ((A 1) (B 2) (C 3) (D 4) (E 5))
Přiřazení výsledného seznamu vytvořeného smyčkou do proměnné:
(setq a (loop for x in '(a b c d e) for y in '(1 2 3 4 5) collect (list x y))) ((A 1) (B 2) (C 3) (D 4) (E 5)) ; tisk hodnoty proměnné a a ((A 1) (B 2) (C 3) (D 4) (E 5))
6. Další příklady použití knihovního makra „loop“: počítané smyčky
V předchozím textu jsme si řekli, že makro loop je možné použít mj. i pro tvorbu takzvaných počítaných smyček, tj. takových smyček, které v každé iteraci zvyšují nebo naopak snižují hodnotu čítače (lokální proměnné platné v rámci smyčky). Syntaxe, kterou makro loop pro tento typ smyček používá, je v mnoha ohledech podobná syntaxi Pascalu či některých verzí starobylého Basicu, jak se ostatně můžete sami přesvědčit na následujících demonstračních příkladech.
Nejprve je uveden základní tvar počítané smyčky se zadáním horní a dolní meze čítače. Hodnota čítače se v tomto případě v každé iteraci zvětšuje o jedničku, jak je to ostatně u počítaných smyček běžné:
(loop for i from 1 to 10 do (print i)) 1 2 3 4 5 6 7 8 9 10
Počítaná smyčka s čítačem, jehož hodnota se v každé iteraci zmenšuje. Povšimněte si použití slova downto, které se vyskytuje například i v Pascalu:
(loop for i from 10 downto 1 do (print i)) 10 9 8 7 6 5 4 3 2 1
U počítaných smyček lze měnit krok, tj. hodnotu, o kterou se čítač smyčky v každé iteraci zvětší nebo naopak zmenší. Zde se použije slovo by:
(loop for i from 1 to 10 by 1.5 do (print i)) 1 2.5 4.0 5.5 7.0 8.5 10.0
V mnoha implementacích programovacího jazyka LISP je podporován i numerický datový typ „zlomek“, což je racionální číslo vyjádřené čitatelem a jmenovatelem odděleným znakem / (lomítko). V počítaných smyčkách lze samozřejmě zlomky využívat, jak je to patrné z následujících dvou příkladů:
(loop for i from 0 to 10 by 3/2 do (print i)) 0 3/2 3 9/2 6 15/2 9 (loop for i from 10 downto 0 by 4/3 do (print i)) 10 26/3 22/3 6 14/3 10/3 2 2/3
Hodnoty čítače je možné v případě potřeby omezit prakticky libovolnou podmínkou. V následujícím příkladu je použit predikát evenp, který vrací hodnotu T (pravda) v tom případě, kdy je parametr tohoto predikátu sudé číslo:
(loop for i from 1 to 10 when (evenp i) do (print i)) 2 4 6 8 10
Hodnoty, kterých postupně nabývá čítač smyčky, lze mít uloženy v seznamu (i když tento příklad je poněkud umělý, protože lze napsat jednodušším způsobem):
(loop with a = '(1 42 3) for i in a do (print i)) 1 42 3
Na závěr si ukážeme způsob zápisu programové smyčky, která současně prochází všemi prvky seznamu a navíc mění hodnotu čítače. Právě tento typ smyčky mnohdy citelně chybí v ostatních programovacích jazycích, které nabízí buď striktně počítanou smyčku nebo smyčku typu for-each:
(loop for x in '(a b c d e) for y from 1 do (format t "~s = ~s~%" y x)) 1 = A 2 = B 3 = C 4 = D 5 = E
7. Základy práce se systémem maker v programovacím jazyku Clojure
V této části článku si ukážeme základní koncepty práce s makry v jazyku Clojure. Základní vlastnosti samotného interpretu jazyka Clojure jsou přitom odvozeny od interpretrů používaných ve většině variant programovacího jazyka LISP, což znamená, že autoři Clojure (resp. přesněji řečeno především jeho původní a dodnes pravděpodobně nejaktivnější autor Rich Hickley) vychází z ověřených technologií, které byly poprvé implementovány již před více než padesáti roky v rámci vývoje LISPu a na něj navazujících jazyků (Scheme). Základem interpretru programovacího jazyka Clojure je, stejně jako v LISPu, smyčka nazývaná REPL (Read-Evaluate-Print-Loop), jejíž název vyplývá z toho, že mohla být relativně jednoduše implementována způsobem ukázaným pod tímto odstavcem. Ostatně z historického pohledu je zajímavé, že nějak podobně vlastně LISP vznikl, když si jeho autor (John McCarthy) uvědomil, že na základě implementace rekurzivní podoby funkce eval a několika dalších pomocných funkcí dokáže vytvořit plnohodnotný programovací jazyk (viz též úvodní článek tohoto seriálu):
(loop (print (eval (read))))
Není bez zajímavosti, že s AST se v LISP/Clojure může manipulovat za použití stejných mechanismů (funkcí/forem/maker), které se používají i při běžném programování – jinými slovy to znamená, že jazyk maker je stále jazykem, v němž se zapisují programy (na rozdíl od zmíněného céčka a C++, kde je jazyk maker zcela odlišný).
8. První písmeno ve zkratce REPL: objekt Reader a jeho makra
Před popisem systému maker v programovacím jazyku Clojure si ještě musíme říci, že ve skutečnosti existují dva typy maker – takzvaná reader macros, neboli makra zabudovaná přímo do objektu/modulu, který načítá formy ze standardního vstupu a potom běžná makra známá i z dalších lispovských programovacích jazyků.
Nejprve se budeme zabývat makry používanými při načítání forem ze standardního vstupu. Důvodů, proč je lepší začít s popisem této skupiny maker je více, například fakt, že tato makra nelze vytvářet a existující makra nelze modifikovat (na rozdíl od výše zmíněného Common Lispu, kde to možné je) a taktéž to, že se tato makra používají v prakticky všech zdrojových kódech, aniž by si vývojáři většinou uvědomovali, že ve svých programech vůbec nějaká makra používají :-) Důvod existence reader maker je jednoduchý – umožňují zkrácení zápisu programů, zajišťují možnost zápisu komentářů (ty totiž nejsou považovány za běžné formy, protože nevrací žádnou hodnotu, ani nil) a taktéž je možné s pomocí tohoto typu maker přidávat k symbolům, seznamům, vektorům, mapám atd. takzvaná metadata.
Zjednodušeně řečeno je možné říci, že reader makra pracují podobně jako preprocesor v programovacích jazycích C a C++, protože text zapisovaný či posílaný na standardní vstup je nejprve těmito makry zpracován a posléze je – stále v textové podobě – poslán funkci read pro parsing a vyhodnocení. Tato makra tedy slouží pro provádění „pouhých“ textových substitucí a nikoli k modifikaci AST, jak je tomu u běžných maker. Která reader makra jsou v programovacím jazyku Clojure podporována, nám prozradí následující tabulka, z níž je patrné, že některá makra provádí skutečně značně jednoduchou činnost, například pouhé odstranění komentářů:
# | Makro | Název | Význam |
---|---|---|---|
1 | ; | comment | umožňuje obejít zápis (comment nějaký text) u komentářů |
2 | \ | character | používané při zápisu znakových literálů |
3 | ^ | metadata | přidání metadat k symbolům, seznamům, vektorům, mapám a množinám |
4 | ' | quote | nahrazuje zápis (quote …) |
5 | ` | syntax-quote | provádí plnou kvalifikaci symbolů + zde lze použít makra ~ a ~@ |
6 | ~ | unquote | zajistí, že se vyhodnotí pouze označená část formy (= provede substituci této části výsledkem) |
7 | ~@ | unquote-splicing | podobné předchozími makru, ovšem výsledná sekvence se vloží ve formě samostatných prvků do „obalující“ sekvence |
8 | @ | deref | nahrazuje zápis (deref …) |
9 | # | dispatch | má různé funkce: donutí reader, aby použil makro z jiné tabulky maker |
S popisem maker vestavěných do objektu Reader začneme jen pozvolna, napřed si totiž popíšeme ta nejjednodušší makra. V jazyku Clojure je nejjednodušším reader makrem s velkou pravděpodobností makro nazvané „comment“ zapisované pomocí znaku ; (středník). Veškerý text, který je zapsaný mezi středníkem a koncem řádku je ignorován, takže toto makro lze použít pro jednoduchý zápis jednořádkových komentářů. Pokud by toto reader makro neexistovalo, muselo by se pro zápis komentářů namísto toho používat normální makro comment, které se však musí zapisovat stejně, jako jakákoli jiná forma, tj. i s kulatými závorkami, což na čitelnosti komentářů určitě nepřidá. Nicméně makro comment má svou nezastupitelnou úlohu, protože pomocí něho můžeme do komentáře „uzavřít“ i delší část kódu – ostatně toto makro je použito i v samotných zdrojových kódech jazyka Clojure.
Podívejme se nyní na několik jednoduchých demonstračních příkladů:
; jednořádkové komentáře user=> ; toto je komentar user=> ; ignorovany v REPL\ user=> ; víceřádkový komentář user=> (comment komentar muze mit nekolik radku) ; povšimněte si, že (comment) vrací hodnotu nil nil user=>
Další velmi často používané reader makro se zapisuje s využitím znaku \ (zpětné lomítko). Toto makro slouží pro zápis znakových konstant (literálů) do zdrojového kódu. Tisknutelné znaky je možné zapsat buď přímo za zpětné lomítko, nebo je možné použít kód libovolného znaku z×Unicode, přičemž kód tohoto znaku musí být zapsán v hexadecimální soustavě za dvojici znaků „\u“:
; běžný tisknutelný znak: user=> \a \a ; další běžný tisknutelný znak: user=> \1 \1 ; znak "u": user=> \u \u ; znak s hexadecimální hodnotou 0x40, neboli 64: user=> \u0040 \@ ; pokus o zápis neplatného znakového literálu: user=> \aa RuntimeException Unsupported character: \aa clojure.lang.Util.runtimeException (Util.java:170) user=>
Posledním „jednoduchým“ reader makrem je makro zapisované s využitím znaku ^. Toto makro slouží k přiřazení metadat k symbolu, seznamu, vektoru, množině či mapě. Základní způsob využití makra ^ je následující:
; nastavení metadat k vektoru user=> ^{:atribut1 "Hodnota1" :atribut2 "Hodnota2"} [1 2 3] [1 2 3] ; uložení vektoru a k němu přiřazeným metadatům ; do proměnné vektor user=> (def vektor ^{:atribut1 "Hodnota1" :atribut2 "Hodnota2"} [1 2 3]) #'user/vektor ; přečtení stavu proměnné vektor user=> vektor [1 2 3] ; přečtení metadat přiřazených k proměnné vektor user=> (meta vektor) {:atribut2 "Hodnota2", :atribut1 "Hodnota1"} user=>
Navíc existuje i alternativní způsob použití makra ^, který slouží pro naplnění jediného atributu s názvem :tag:
; uložení vektoru a k němu přiřazeným metadatům ; do proměnné vektor user=> (def vektor ^"xyzzy" [1 2 3]) #'user/vektor ; přečtení stavu proměnné vektor user=> vektor [1 2 3] ; přečtení metadat přiřazených k proměnné vektor user=> (meta vektor) {:tag "xyzzy"} user=>
Makro „deref“ má již složitější chování. Může být použito pro přečtení hodnoty, přesněji řečeno stavu reference a v programovacím jazyku Clojure se používá u mnoha typů referencí (referenčních typů):
# | Referenční typ |
---|---|
1 | @ref |
2 | @agent |
3 | @var |
4 | @atom |
5 | @delay |
6 | @future |
7 | @promise |
Chování makra @ se však u různých typů referencí liší. Zatímco @ref, @var či @atom pouze vrátí aktuální stav reference, v případě použití @future, @promise či @agent se ve skutečnosti musí počkat na dokončení (asynchronního) výpočtu, který běží v jiném vláknu. Pro jistotu si připomeňme tabulku s funkcemi a makry použitými při práci s nejdůležitějšími referenčními typy:
# | Typ | Var | Ref | Atom | Agent |
---|---|---|---|---|---|
1 | Vytvoření | (def name value) | (ref value) | (atom value) | (agent value) |
2 | Nastavení hodnoty | (set! name value) | (ref-set ref value) | (reset! atom value) | × |
3 | Aplikace funkce | × | (alter ref funkce) | (swap! atom funkce) | (send agent funkce) |
4 | Čtení hodnoty | name | @ref | @atom | @agent |
Demonstrační příklad na použití referencí typu ref, tedy „normálních“ proměnných platných v rámci aktuálního jmenného prostoru (ve skutečnosti je s referencemi typu ref možné provádět mnoho operací, které by s běžnými proměnnými nebyly možné):
; vytvoření refu user=> (def my-ref (ref 42)) #'user/my-ref ; vytvoření refu user=> (def string-ref (ref "Hello world")) #'user/string-ref ; přečtení refu pomocí makra @ user=> @my-ref 42 ; přečtení refu pomocí makra @ user> @string-ref "Hello world" user=>
Demonstrační příklad na použití atomů a samozřejmě i makra @:
; vytvoření nového atomu user=> (def x (atom 42)) #'user/x ; globální symbol x je navázán ; na atom a nikoli na stav identity ; (=hodnotu) user=> x #<Atom@61a907: 42> ; pro získání aktuálního stavu ; je nutné použít dereferenci user=> (deref x) 42 ; namísto (deref x) se používá ; makro preprocesoru @ user=> @x 42 ; atomická změna stavu identity user=> (reset! x 10) 10 user=> (reset! x (+ 1 2 3)) 6 user=> @x 7 ; další možnost atomické změny ; stavu identity - nyní přes funkci ; aplikovanou na atom a popř. i další ; parametry user=> (swap! x + 1) 7 user=> @x 7 user=>
Příklad vytvoření agenta, poslání funkce agentovi a čekání na dokončení výpočtu právě s použitím makra @:
; vytvoření agenta user=> (def my-agent (agent 0)) #'user/my-agent ; poslání funkce agentovi (6 je druhý parametr funkce) user=> (send my-agent + 6) #<Agent@18622f3: 0> ; poslání funkce agentovi (7 je druhý parametr funkce) user=> (send my-agent * 7) #<Agent@18622f3: 6> ; dereference - získání nového stavu - pomocí makra @ user=> @my-agent 42 user=>
Další příklad s makrem @, tentokrát použitým pro objekty future:
; vytvoření objektu typu future a spuštění paralelního vlákna user=> (def future_fibonacci1 (future (fibonacci 35))) #'user/future_fibonacci1 ; vytvoření dalšího objektu typu future a spuštění paralelního vlákna user=> (def future_fibonacci2 (future (fibonacci 35))) #'user/future_fibonacci2 ; čekání na dokončení prvního paralelně běžícího výpočtu ; - zde se využívá makro @ user=> @future_fibonacci1 9227465 ; čekání na dokončení druhého paralelně běžícího výpočtu ; - zde se využívá makro @ user=> @future_fibonacci2 9227465 user=>
Poslední demonstrační příklad: makro @ a objekty typu promise:
; vytvoření objektu typu promise ; a navázání na symbol promise-test user=> (def promise-test (promise)) #'user/promise-test ; nastavení hodnoty user=> (deliver promise-test 42) #<core$promise$reify__6153@1318b: 42> ; získání nastavené hodnoty pomocí makra @ user=> @promise-test 42 user=>
9. Makra „quote“ a „syntax-quote“
Konečně se dostáváme k zajímavějším a užitečnějším makrům, které přibližují možnosti Clojure Common Lispu i dalším pokročilejším lispovským jazykům, pochopitelně včetně Racketu. Jedno z nejdůležitějších a nejčastěji používaných maker se jmenuje „quote“ a zapisuje se pomocí apostrofu. Toto makro zakazuje vyhodnocování seznamů, protože pokud by objekt reader načetl formu ve tvaru (a b c), předal by ji do funkce eval, kde by se tato forma vyhodnotila jako volání funkce a s parametry b a c. Pokud však reader načte formu '(a b c), ztransformuje ji do tvaru (quote (a b c)), přičemž quote je speciální forma zakazující vyhodnocení. Na většinu ostatních objektů kromě seznamů nemá makro „quote“ většinou žádný vliv:
; zde nemá quote žádný vliv user=> '42 42 ; zákaz vyhodnocení seznamů jako funkce user=> '(1 2 3) (1 2 3) ; zde nemá quote žádný vliv user=> '[1 2 3] [1 2 3] ; stejné jako předchozí forma user=> [1 2 3] [1 2 3] ; zákaz vyhodnocení seznamů jako funkce user=> '(* 6 7) (* 6 7) ; zde se však seznam vyhodnotí jako funkce * user=> (* 6 7) 42 user=>
Kromě makra „quote“ ještě objekt reader rozeznává poněkud komplikovanější makro nazývané „syntax-quote“, které se zapisuje pomocí zpětného apostrofu: `. Chování tohoto makra se liší podle toho, s jakým typem objektu je použito, ovšem ve všech případech se makro chová tak, aby nedocházelo k vyhodnocení jeho argumentů, popř. ani k vyhodnocení vnořených forem.
V následujících příkladech dochází k jednoduchému zákazu vyhodnocení předané formy:
user=> `42 42 user=> `(1 2 3) (1 2 3) user=> `[1 2 3] [1 2 3] user=>
Dále toho makro dokáže nahradit zjednodušené jméno symbolu jeho plně kvalifikovaným jménem. Nejlépe si to opět ukážeme na několika příkladech:
user=> `seq clojure.core/seq user=> `map clojure.core/map user=> `Integer/valueOf java.lang.Integer/valueOf user=>
Zákaz vyhodnocení a současně i náhrada zjednodušeného jména symbolu na plně kvalifikované jméno se projeví tím, že se následující seznamy (a vektor na konci) nevyhodnotí jako funkce, ale například funkce * se nahradí plným jménem:
user=> `(* 6 7) (clojure.core/* 6 7) user=> `(str "Hello" "world") (clojure.core/str "Hello" "world") user=> `[* seq str xyzzy neznamy] [clojure.core/* clojure.core/seq clojure.core/str user/xyzzy user/neznamy] user=>
10. Makra „unquote“ a „unquote-splicing“
Makro nazvané „unquote“, které se zapisuje s využitím znaku ~ (tilda), dokáže vynutit vyhodnocení určité části výrazu, a to tehdy, pokud je tento výraz umístěn v makru ` (syntax-quote), nikoli však ' (quote).
Nejprve si ukažme způsob zápisu tohoto makra i to, jaký má toto makro vliv na zapisované výrazy:
; makro quote zakáže vyhodnocení celého seznamu user=> '(1 2 (* 6 7) (/ 4 2)) (1 2 (* 6 7) (/ 4 2)) ; makro syntax-quote zakáže vyhodnocení taktéž a ; současně provede náhradu jmen funkcí za jejich plný tvar user=> `(1 2 (* 6 7) (/ 4 2)) (1 2 (clojure.core/* 6 7) (clojure.core// 4 2)) ; pomocí ~ vynutíme vyhodnocení podvýrazu (* 6 7) user=> `(1 2 ~(* 6 7) (/ 4 2)) (1 2 42 (clojure.core// 4 2)) ; pomocí ~ vynutíme vyhodnocení podvýrazu (/ 4 2) user=> `(1 2 (* 6 7) ~(/ 4 2)) (1 2 (clojure.core/* 6 7) 2) ; pomocí dvou ~ vynutíme vyhodnocení obou podvýrazů user=> `(1 2 ~(* 6 7) ~(/ 4 2)) (1 2 42 2) user=>
Podobným způsobem pracuje i makro zapisované pomocí dvou znaků ~@, ovšem to navíc ještě provádí „zplošťování seznamů“. Prozatím si chování tohoto makra ukážeme na velmi jednoduchém umělém příkladu:
; uživatelsky definovaný seznam user=> (def s '(1 2 3)) #'user/s ; makro quote zcela zakáže vyhodnocování user=> '(1 2 3 (cons s s)) (1 2 3 (cons s s)) ; makro syntax-quote taktéž, ovšem ještě nahradí ; všechny symboly jejich plnými jmény user=> `(1 2 3 (cons s s)) (1 2 3 (clojure.core/cons user/s user/s)) ; vynutíme si vyhodnocení podvýrazu (cons s s) ; který vrací ((1 2 3) 1 2 3) user=> `(1 2 3 ~(cons s s)) (1 2 3 ((1 2 3) 1 2 3)) ; dtto, ovšem seznam, jenž je výsledkem (cons s s) ; je zploštěn (jakoby je odstraněna jedna úroveň zanoření) user=> `(1 2 3 ~@(cons s s)) (1 2 3 (1 2 3) 1 2 3) user=>
Podívejme se nyní na jednoduché uživatelské makro vytvořené s využitím formy defmacro. V samotném makru použijeme výše zmíněná makra ` (syntax quote) a ~ (unquote):
(defmacro trace1 [vyraz] `(let [x ~vyraz] (println '~vyraz "=" x) x))
Aby makro fungovalo zcela správně, musíme umět vytvořit lokální symbol s unikátním jménem. Pro tento účel se používá (tak jako v mnoha dalších situacích) znak křížku zapisovaný ZA nějakým symbolem. Clojure zápis symbol# expanduje na symbol_generované_číslo, které bude unikátní, což je přesně to, co potřebujeme. Ve třetí verzi našeho makra tedy nahradíme x za x#:
(defmacro trace2 [vyraz] `(let [x# ~vyraz] (println '~vyraz "=" x#) x#))
Nyní tedy již známe téměř všechny nástroje používané při tvorbě maker v Clojure:
Znak | Význam při tvorbě maker |
---|---|
` | většinou uzavírá celé tělo makra, tj. šablonu vytvářeného výrazu |
~ | ruší význam znaku `, ovšem pouze pro jediný symbol uvedený ihned za tímto znakem |
# | symbol zapsaný před tímto znakem bude doplněn takovým způsobem, aby se jednalo o unikátní jméno |
11. Makra v programovacím jazyku Racket
Ve druhé části dnešního článku je ukázán základní způsob tvorby maker v programovacím jazyku Racket. Bude se, alespoň z hlediska teorie, jednat o stejný koncept, s nímž jsme se seznámili v předchozích kapitolách (ostatně Racket je i přes svá mnohá rozšíření stále lispovským jazykem), ovšem s tím rozdílem, že se s makry v Racketu pracuje jednodušším a taktéž bezpečnějším způsobem – samotný makrosystém nás totiž v případě potřeby dokáže odstínit od některých problémů, na něž jsme narazili výše (lokální proměnné v expanzi makra atd.). Dále je možné v Racketu vytvářet makra s proměnným množstvím argumentů, což je sice možné i v Common Lispu či Clojure, ovšem koncept použitý v Racketu je velmi snadno pochopitelný a následně i použitelný (vlastně se syntakticky příliš neliší od funkcí s proměnným počtem parametrů, které jsme si popsali v předchozí části tohoto seriálu).
12. Vytvoření makra s využitím define-syntax-rule
Nejprve se seznámíme se způsobem tvorby maker založených na formě pojmenované define-syntax-rule. Tato makra se nazývají pattern-based macros, což je název plynoucí z toho, že se expanze makra řídí sadou vzorků/pravidel (minimálně jednoho vzorku). Základní použití formy define-syntax-rule vypadá podobně, jako tomu bylo ve výše zmíněné formě defmacro:
(define-syntax-rule (square x) (* x x))
Samotný zápis se do značné míry podobá zápisu běžné funkce (alespoň z pohledu syntaxe), ovšem sémantika je odlišná:
- Za jménem formy define-syntax-rule se v závorce nachází takzvaný vzorek (pattern). Každý vzorek začíná jménem makra následovaného názvy parametrů. Podle počtu parametrů se určuje, jaká část makra bude v daném kontextu expandována.
- Za vzorkem/vzorky následuje šablona makra (template), tj. (zjednodušeně a nepřesně řečeno) kód, na který je makro expandováno.
V našem jednoduchém případě bude volání makra:
(display (square 10)) 100
expandováno na:
(* 10 10)
což asi není příliš překvapivé a v tomto případě ani užitečné.
Podívat se pochopitelně můžeme i na expanzi tohoto makra, a to buď celou expanzi až na základní symboly jazyka nebo pouze na expanzi makra (bez dalšího zpracování podforem):
(define-syntax-rule (square x) (* x x)) (display (square 10)) (newline) (display (expand-once '(square 10))) (newline) (display (expand '(square 10))) (newline)
Výsledkem bude výsledek výrazu, na který se makro expandovalo, expanze samotného makra i celá expanze na primární prvky jazyka:
100 #<syntax:/home/tester/test..rkt:2:4 (* 10 10)> #<syntax:/home/tester/test..rkt:2:4 (#%app * (quote 10) (quote 10))>
Použití proměnné namísto konstanty 10:
(define-syntax-rule (square x) (* x x)) (define foobar 10) (display (square foobar)) (newline) (display (expand-once '(square foobar))) (newline) (display (expand '(square foobar))) (newline)
S výsledkem:
100 #<syntax:/home/tester/test.rkt:2:4 (* foobar foobar)> #<syntax:/home/tester/test.rkt:2:4 (#%app * foobar foobar)>
Pozor ovšem na to, že se parametr v expandovaném makru vyhodnocuje dvakrát:
(display (expand-once '(square (+ 1 2)))) #<syntax:readline-input:2:3 (* (+ 1 2) (+ 1 2))>
Úprava tohoto problému může vypadat například takto:
(define-syntax-rule (square x) (let ([tmp x]) (* tmp tmp))) (display (square 10)) (newline) (display (expand-once (square 10))) (newline) (display (expand-once '(square 10))) (newline) (display (expand-once '(square 10))) (newline) (display (expand '(square 10))) (newline) (display (expand-once '(square (+ 1 2)))) (newline)
S výsledky:
100 #<syntax (quote 100)> #<syntax:readline-input:2:4 (let ((tmp 10)) (* tmp tmp))> #<syntax:readline-input:2:4 (let ((tmp 10)) (* tmp tmp))> #<syntax:readline-input:2:4 (let-values (((tmp) (quote 10))) (#%app * tmp tmp))> #<syntax:readline-input:2:4 (let ((tmp (+ 1 2))) (* tmp tmp))>
13. Syntaktické objekty, použití syntax->datum
V předchozí kapitole jsme mohli vidět, že pokud si necháme vypsat expandované makro, vrátí se hodnota začínající na #<syntax::
(display (expand-once '(square (+ 1 2)))) #<syntax:readline-input:2:4 (let ((tmp (+ 1 2))) (* tmp tmp))>
Jedná se o takzvané syntaktické objekty, které je možné zapisovat i přímo s pomocí znaku #. Ovšem nás bude nyní spíše zajímat, jak z takového objektu získat čitelnou podobu expandovaného kódu. K tomuto účelu slouží forma syntax->datum, kterou lze použít například takto:
(define-syntax-rule (square x) (* x x)) (display (square 10)) (newline) (display (syntax->datum (expand-once (square 10)))) (newline) (display (syntax->datum (expand-once '(square 10)))) (newline) (display (syntax->datum (expand-once '(square 10)))) (newline) (display (syntax->datum (expand '(square 10)))) (newline) (display (syntax->datum (expand-once '(square (+ 1 2))))) (newline)
S krásně čitelnými výsledky expanze makra:
100 (quote 100) (* 10 10) (* 10 10) (#%app * (quote 10) (quote 10)) (* (+ 1 2) (+ 1 2))
Podobně si můžeme nechat vypsat expandovanou podobu vylepšené varianty makra square:
(define-syntax-rule (square x) (let ([tmp x]) (* tmp tmp))) (display (square 10)) (newline) (display (syntax->datum (expand-once (square 10)))) (newline) (display (syntax->datum (expand-once '(square 10)))) (newline) (display (syntax->datum (expand-once '(square 10)))) (newline) (display (syntax->datum (expand '(square 10)))) (newline) (display (syntax->datum (expand-once '(square (+ 1 2))))) (newline)
Tentokrát pochopitelně s odlišnými výsledky, protože se v expandovaném makru objeví forma let:
100 (quote 100) (let ((tmp 10)) (* tmp tmp)) (let ((tmp 10)) (* tmp tmp)) (let-values (((tmp) (quote 10))) (#%app * tmp tmp)) (let ((tmp (+ 1 2))) (* tmp tmp))
14. Makro pro prohození obsahu dvou proměnných
Ukažme si tedy nepatrně složitější makro, které je mimochodem popsáno i v dokumentaci programovacího jazyka Racket. Toto makro se jmenuje swap a slouží k prohození obsahu dvou proměnných s využitím lokální proměnné ve formě dočasného úložiště:
(define-syntax-rule (swap x y) (let ([tmp x]) (set! x y) (set! y tmp)))
Příklad použití makra:
(swap foo bar)
Popř. v uceleném příkladu s dvojicí lokálních proměnných:
(let [(x 10) (y 20)] (swap x y) (display x) (newline) (display y) (newline)) 20 10
Toto volání makra je expandováno na:
(let ([tmp foo]) (set! foo bar) (set! bar tmp))
Otestování s dvojicí globálních proměnných:
(define-syntax-rule (swap x y) (let ([tmp x]) (set! x y) (set! y tmp))) (define foo 10) (define bar 20) (swap foo bar) (display foo) (newline) (display bar) (newline) (display (expand-once '(swap foo bar))) (newline)
Výsledky:
20 10 #<syntax:/home/tester/test.rkt:2:4 (let ((tmp foo)) (set! foo bar) (set! bar tmp))>
Co se však stane v případě, že budeme chtít prohodit obsah proměnných tmp a baz? Mohlo by se zdát, že v tomto případě dojde ke kolizi jména parametru makra a jména lokální proměnné v expandovaném makru:
(let ([tmp tmp]) (set! tmp baz) (set! baz tmp))
Ve skutečnosti ovšem makrosystém programovacího jazyka Racket tomuto problému automaticky zabrání, protože dokáže lokální proměnné v expandovaném makru přejmenovat takovým způsobem, aby byly unikátní:
(let ([tmp_1 tmp]) (set! tmp baz) (set! baz tmp_1))
To však není zdaleka vše, protože se podobným způsobem zabrání tomu, aby se použila pozměněná hodnota symbolu set!. Pokud totiž jednu z proměnných nazveme právě set!, bude expandované makro vypadat takto:
(let ([tmp_1 set1_1]) (set! set1_1 baz) (set! baz tmp_1))
Pokud by k této záměně nedošlo, nebylo by možné funkci set! zavolat.
Otestování:
(define-syntax-rule (swap x y) (let ([tmp x]) (set! x y) (set! y tmp))) (let ([set! 10] [baz 20]) (swap set! baz) (display set!) (newline) (display baz) (newline))
Výsledek je opět předvídatelný:
20 10
15. Makro s volitelnou aritou
V programovacím jazyku Racket je možné vytvářet makra s volitelnou (proměnnou) aritou, podobně jako je možné vytvářet funkce s proměnnou aritou. Asi nejtypičtějším příkladem mohou být makra nazvaná and a or, které slouží k podmíněnému vyhodnocení zapsaných výrazů. Vyhodnocení probíhá zleva doprava a ve chvíli, kdy je již výsledek výrazu známý (mezivýsledek je #f u and nebo #t u or), vyhodnocení je ihned ukončeno a další parametry se nijak nevyhodnocují. Díky tomu je možné tato makra použít pro řízení běhu programu a navíc nelze and ani or realizovat běžnou funkcí (tam by se naopak nejprve vyhodnotily všechny parametry, což nechceme).
Problém ovšem spočívá v tom, že and a or nejsou binární operátory (tak jako je tomu v mnoha mainstreamových jazycích), ale formy akceptující libovolný počet parametrů, přičemž pro různé počty parametrů se může provádět odlišný výpočet. K tomuto účelu slouží forma (taktéž makro) nazvaná syntax-rules, které můžeme předat libovolný počet vzorků volání makra i jeho expandované podoby. Například budeme požadovat, aby se zavolání or bez parametrů expandovalo přímo na hodnotu #f, zavolání or s jedním parametrem přímo na hodnotu tohoto parametru a při zavolání se dvěma parametry bude výsledek buď první hodnota (pokud je #t) nebo naopak hodnota druhá:
(define-syntax or (syntax-rules () [(or) #f] [(or x) x] [(or x y) (let ([z x]) (if z z y))]))
Chování tohoto makra si můžeme velmi snadno ověřit, například na několika variantách volání bez parametrů, s jedním parametrem a se dvěma parametry:
(display (or)) (newline) (display (or #t)) (newline) (display (or #f)) (newline) (display (or #t #f)) (newline) (display (or #f #f)) (newline)
Navíc se taktéž můžeme podívat na způsob expanze tohoto makra:
(display (expand-once '(or))) (newline) (display (expand-once '(or #f))) (newline) (display (expand-once '(or #f #f))) (newline)
S výsledky:
#<syntax:/home/tester/test.rkt:3:10 #f> #<syntax #f> #<syntax:/home/tester/test.rkt:5:14 (let ((z #f)) (if z z #f))>
Čitelnější podobu expandovaného makra získáme opět přes syntax->datum:
(display (syntax->datum (expand-once '(or)))) (display (syntax->datum (expand-once '(or #f)))) (display (syntax->datum (expand-once '(or #t)))) (display (syntax->datum (expand-once '(or #f #f)))) (display (syntax->datum (expand-once '(or #f #t))))
Nyní se vypíše:
#f #f #t (let ((z #f)) (if z z #f)) (let ((z #f)) (if z z #t))
16. Použití … v makru
V případě, že budeme chtít makro or modifikovat takovým způsobem, aby pracovalo skutečně s libovolným množstvím argumentů, můžeme pro tento účel použít … (tři tečky). Nejprve si však ukažme, jak Racket reaguje na situaci, kdy se makru předává špatný počet parametrů, který neodpovídá žádnému pravidlu. Použijeme původní podobu makra:
(define-syntax or (syntax-rules () [(or) #f] [(or x) x] [(or x y) (let ([z x]) (if z z y))]))
Makro budeme volat se třemi parametry (takové pravidlo neexistuje):
(display (or #f #f #t))
V tomto případě se vypíše chybové hlášení „bad syntax“ společně s výrazem, který je z pohledu interpretru programovacího jazyka Racket chybný:
or: bad syntax in: (or #f #f #f)
V definici makra ovšem můžeme použít … (trojici teček), a to jak v samotném vzorku, tak i v šabloně popisující, jak se má makro expandovat:
(define-syntax or (syntax-rules () [(or) #f] [(or x) x] [(or x y) (let ([z x]) (if z z y))] [(or x y ...) (or x (or y ...))]))
V tomto případě se při expanzi makra nahradí trojice teček za ty parametry, které nejsou explicitně pojmenovány. Jinými slovy to znamená, že se zvýrazněný řádek uplatní u volání makra se třemi a více parametry. Volání se nahradí expanzí makra, nyní ovšem bez prvního parametru a v případě potřeby se expanze provádí rekurzivně – to vše ve chvíli, kdy je makro načteno objektem typu Reader, nikoli v čase běhu.
Chování si můžeme odzkoušet:
(display (or #f #f #t))
#t
A samozřejmě si můžeme zobrazit i expandované makro:
(syntax->datum (expand-once '(or #t #f #t #f #t #f))) '(or #t (or #f #t #f #t #f))
Plná expanze makra v čitelné podobě:
(syntax->datum (expand '(or #t #f #t #f #t #f))) '(let-values (((z) '#t)) (if z z (let-values (((z) '#f)) (if z z (let-values (((z) '#t)) (if z z (let-values (((z) '#f)) (if z z (let-values (((z) '#t)) (if z z '#f))))))))))
17. Původní (nehygienická) makra v Racketu
V programovacím jazyku Racket se důrazně doporučuje používat hygienická makra založená na vzorcích a šablonách, tedy makra popsaná v předchozích kapitolách. Ovšem pro účely portace zdrojových kódů z dalších dialektů Scheme nebo (Common) Lispu existuje balíček, který programátorům nabízí i formy defmacro, define-macro atd. Tento modul je popsaný na stránce https://docs.racket-lang.org/compatibility/defmacro.html. Jedná se o klasicky pojatá makra, která dokážou pracovat přímo se stromem reprezentovaným S-výrazem. Kvůli tomu je nutné používat formy pro quote, quasiquote a unquote, které jsme si představili při popisu Common Lispovských maker.
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/lisp-families.git (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
19. Literatura
- Peter Seibel
„Practical Common Lisp“
2009 - Paul Graham
„ANSI Common Lisp“
1995 - Gerald Gazdar
„Natural Language Processing in Lisp: An Introduction to Computational Linguistics“
1989 - Peter Norvig
„Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp“
1991 - Alex Mileler et.al.
„Clojure Applied: From Practice to Practitioner“
2015 - „Living Clojure: An Introduction and Training Plan for Developers“
2015 - Dmitri Sotnikov
„Web Development with Clojure: Build Bulletproof Web Apps with Less Code“
2016 - McCarthy
„Recursive functions of symbolic expressions and their computation by machine, part I“
1960 - R. Kent Dybvig
„The Scheme Programming Language“
2009 - Max Hailperin
„Concrete Abstractions“
1998 - Guy L. Steele
„History of Scheme“
2006, Sun Microsystems Laboratories - Kolář J., Muller K.:
„Speciální programovací jazyky“
Praha 1981 - „AutoLISP Release 9, Programmer's reference“
Autodesk Ltd., October 1987 - „AutoLISP Release 10, Programmer's reference“
Autodesk Ltd., September 1988 - McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I.
„LISP 1.5 Programmer's Manual“
MIT Press. ISBN 0 262 130 1 1 4 - Carl Hewitt; Peter Bishop and Richard Steiger
„A Universal Modular Actor Formalism for Artificial Intelligence“
1973 - Feiman, J.
„The Gartner Programming Language Survey (October 2001)“
Gartner Advisory - Harold Abelson, Gerald Jay Sussman, Julie Sussman:
Structure and Interpretation of Computer Programs
MIT Press. 1985, 1996 (a možná vyšel i další přetisk) - Paul Graham
On Lisp
Prentice Hall, 1993
Dostupné online na stránce http://www.paulgraham.com/onlisptext.html - David S. Touretzky
Common LISP: A Gentle Introduction to Symbolic Computation (Dover Books on Engineering)
- Peter Norvig
Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp - Patrick Winston, Berthold Horn
Lisp (3rd Edition)
ISBN-13: 978–0201083194, ISBN-10: 0201083191 - Matthias Felleisen, David Van Horn, Dr. Conrad Barski
Realm of Racket: Learn to Program, One Game at a Time!
ISBN-13: 978–1593274917, ISBN-10: 1593274912
20. Odkazy na Internetu
- Racket: Macros
https://www.it.uu.se/edu/course/homepage/avfunpro/ht13/lectures/Racket-3-Macros.pdf - Beautiful Racket / explainers: Macros
https://beautifulracket.com/explainer/macros.html - Macros (dokumentace k Racketu)
https://docs.racket-lang.org/guide/macros.html - Model syntaxe jazyka Racket
https://docs.racket-lang.org/reference/syntax-model.html - Syntax Objects
https://docs.racket-lang.org/guide/stx-obj.html - Tech behind Tech: Clojure Macros Simplified
http://techbehindtech.com/2010/09/28/clojure-macros-simplified/ - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - Beautiful Racket: an introduction to language-oriented programming using Racket
https://beautifulracket.com/ - Stránky projektu Racket
https://racket-lang.org/ - Dokumentace k projektu Racket
https://docs.racket-lang.org/index.html - Seznam dostupných balíčků pro Racket
https://pkgs.racket-lang.org/ - Racket na Wikipedii
https://en.wikipedia.org/wiki/Racket_(programming_language) - Blogy o Racketu a navazujících technologiích
https://blog.racket-lang.org/ - Prográmky psané v Racketu na RosettaCode
http://rosettacode.org/wiki/Category:Racket - Fear of Macros
https://www.greghendershott.com/fear-of-macros/ - Rackjure
https://github.com/greghendershott/rackjure - Matthew Flatt’s proposal to change Racket’s s-expressions based syntax to infix representation creates a stir in the community
https://hub.packtpub.com/matthew-flatts-proposal-to-change-rackets-s-expressions-based-syntax-to-infix-representation-creates-a-stir-in-the-community/ - Racket News
https://racket-news.com/ - Racket: Lisp for learning
https://lwn.net/Articles/795385/ - Future of Racket
https://www.greghendershott.com/2019/07/future-of-racket.html - Kawa: Compiling Scheme to Java
https://www.mit.edu/afs.new/sipb/project/kawa/doc/kawa-tour.html - Kawa in Languages shootout
http://per.bothner.com/blog/2010/Kawa-in-shootout/ - Kawa 2.0 Supports Scheme R7RS
https://developers.slashdot.org/story/14/12/13/2259225/kawa-20-supports-scheme-r7rs/ - Kawa — fast scripting on the Java platform
https://lwn.net/Articles/623349/ - Tail call (a její optimalizace)
https://en.wikipedia.org/wiki/Tail_call - SLIME (Wikipedia)
http://en.wikipedia.org/wiki/SLIME - slime.vim
http://s3.amazonaws.com/mps/slime.vim - What are the best scheme implementations?
https://www.slant.co/topics/5282/~scheme-implementations - Bigloo homepage
http://www-sop.inria.fr/mimosa/fp/Bigloo/ - FTP s tarbally Bigloo
ftp://ftp-sop.inria.fr/indes/fp/Bigloo - GOTO 2018 • Functional Programming in 40 Minutes • Russ Olsen
https://www.youtube.com/watch?v=0if71HOyVjY - TinyScheme (stránka na Sourceforge)
http://tinyscheme.sourceforge.net/home.html - Embedding Tiny Scheme in a Game
http://www.silicondelight.com/embedding-tiny-scheme-in-a-game/ - Embedding Scheme for a game mission scripting DSL
http://carloscarrasco.com/embedding-scheme-for-a-game-mission-scripting-dsl.html - Všechny verze TinyScheme na SourceForge
https://sourceforge.net/projects/tinyscheme/files/tinyscheme/ - Fork TinyScheme na GitHubu
https://github.com/yawnt/tinyscheme - Ackermannova funkce
https://cs.wikipedia.org/wiki/Ackermannova_funkce - Ackermann function na Rosetta Code
https://rosettacode.org/wiki/Ackermann_function#Scheme - Success Stories (lisp.org)
https://lisp-lang.org/success/ - Allegro Common Lisp Success Stories
https://franz.com/success/ - Clojure Success Stories
https://clojure.org/community/success_stories - Scheme Quick Reference
https://www.st.cs.uni-saarland.de/edu/config-ss04/scheme-quickref.pdf - Slajdy o Scheme (od slajdu číslo 15)
https://docs.google.com/presentation/d/1abmDnKjrq1tcjGvvRNAKhOiSTSE2lyagtcEPal07Gbo/edit - Scheme Cheat Sheet
https://github.com/smythp/scheme-cheat-sheet - Embedding Lua, embedding Guile
http://puntoblogspot.blogspot.com/2013/04/embedding-lua-embedding-guile.html - Lambda Papers
https://en.wikisource.org/wiki/Lambda_Papers - Revised7Report on the Algorithmic Language Scheme
https://small.r7rs.org/attachment/r7rs.pdf - Video Lectures (MIT, SICP 2005)
https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6–001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/ - Why is Scheme my first language in university?
https://softwareengineering.stackexchange.com/questions/115252/why-is-scheme-my-first-language-in-university - The Perils of JavaSchools
https://www.joelonsoftware.com/2005/12/29/the-perils-of-javaschools-2/ - How to Design Programs, Second Edition
https://htdp.org/2019–02–24/index.html - LilyPond
http://lilypond.org/ - LilyPond — Extending (přes Scheme)
http://lilypond.org/doc/v2.18/Documentation/extending/scheme-tutorial - Scheme in LilyPond
http://lilypond.org/doc/v2.18/Documentation/extending/scheme-in-lilypond - GnuCash
http://www.gnucash.org/ - Custom Reports (in GNU Cash)
https://wiki.gnucash.org/wiki/Custom_Reports - Program by Design
https://programbydesign.org/ - SchemePy
https://pypi.org/project/SchemePy/ - LISP FQA: Section – [1–5] What is the „minimal“ set of primitives needed for a Lisp interpreter?
http://www.faqs.org/faqs/lisp-faq/part1/section-6.html - femtolisp
https://github.com/JeffBezanson/femtolisp - (How to Write a (Lisp) Interpreter (in Python))
http://norvig.com/lispy.html - Repositář s Guile Emacsem
http://git.hcoop.net/?p=bpt/guile.git - Interacting with Guile Compound Data Types in C
http://www.lonelycactus.com/guilebook/x1555.html - Calling Guile functions from C
http://www.lonelycactus.com/guilebook/c1204.html#SECCALLGUILEFUNC - Arrays, and other compound data types
http://www.lonelycactus.com/guilebook/charrays.html - Interacting with Guile Compound Data Types in C
http://www.lonelycactus.com/guilebook/x1555.html - Guile Reference Manual
https://www.gnu.org/software/guile/manual/html_node/index.html - Scheme: Summary of Common Syntax
https://www.gnu.org/software/guile/manual/html_node/Syntax-Summary.html#Syntax-Summary - Scripting with Guile: Extension language enhances C and Scheme
https://www.ibm.com/developerworks/library/l-guile/index.html - Having fun with Guile: a tutorial
http://dustycloud.org/misc/guile-tutorial.html - Guile: Loading Readline Support
https://www.gnu.org/software/guile/manual/html_node/Loading-Readline-Support.html#Loading-Readline-Support - lispy
https://pypi.org/project/lispy/ - Lython
https://pypi.org/project/Lython/ - Lizpop
https://pypi.org/project/lizpop/ - Budoucnost programovacích jazyků
http://www.knesl.com/budoucnost-programovacich-jazyku - LISP Prolog and Evolution
http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html - List of Lisp-family programming languages
https://en.wikipedia.org/wiki/List_of_Lisp-family_programming_languages - clojure_py na indexu PyPi
https://pypi.python.org/pypi/clojure_py - PyClojure
https://github.com/eigenhombre/PyClojure - Hy na GitHubu
https://github.com/hylang/hy - Hy: The survival guide
https://notes.pault.ag/hy-survival-guide/ - Hy běžící na monitoru terminálu společnosti Symbolics
http://try-hy.appspot.com/ - Welcome to Hy’s documentation!
http://docs.hylang.org/en/stable/ - Hy na PyPi
https://pypi.org/project/hy/#description - Getting Hy on Python
https://lwn.net/Articles/596626/ - Programming Can Be Fun with Hy
https://opensourceforu.com/2014/02/programming-can-fun-hy/ - Přednáška o projektu Hy (pětiminutový lighttalk)
http://blog.pault.ag/day/2013/04/02 - Hy (Wikipedia)
https://en.wikipedia.org/wiki/Hy - GNU Emacs Lisp Reference Manual: Point
https://www.gnu.org/software/emacs/manual/html_node/elisp/Point.html - GNU Emacs Lisp Reference Manual: Narrowing
https://www.gnu.org/software/emacs/manual/html_node/elisp/Narrowing.html - GNU Emacs Lisp Reference Manual: Functions that Create Markers
https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Markers.html - GNU Emacs Lisp Reference Manual: Motion
https://www.gnu.org/software/emacs/manual/html_node/elisp/Motion.html#Motion - GNU Emacs Lisp Reference Manual: Basic Char Syntax
https://www.gnu.org/software/emacs/manual/html_node/elisp/Basic-Char-Syntax.html - Elisp: Sequence: List, Array
http://ergoemacs.org/emacs/elisp_list_vs_vector.html - Elisp: Property List
http://ergoemacs.org/emacs/elisp_property_list.html - Elisp: Hash Table
http://ergoemacs.org/emacs/elisp_hash_table.html - Elisp: Association List
http://ergoemacs.org/emacs/elisp_association_list.html - The mapcar Function (An Introduction to Programming in Emacs Lisp)
https://www.gnu.org/software/emacs/manual/html_node/eintr/mapcar.html - Anaphoric macro
https://en.wikipedia.org/wiki/Anaphoric_macro - Some Common Lisp Loop Macro Examples
https://www.youtube.com/watch?v=3yl8o6r_omw - A Guided Tour of Emacs
https://www.gnu.org/software/emacs/tour/ - The Roots of Lisp
http://www.paulgraham.com/rootsoflisp.html - Evil (Emacs Wiki)
https://www.emacswiki.org/emacs/Evil - Evil (na GitHubu)
https://github.com/emacs-evil/evil - Evil (na stránkách repositáře MELPA)
https://melpa.org/#/evil - Evil Mode: How I Switched From VIM to Emacs
https://blog.jakuba.net/2014/06/23/evil-mode-how-to-switch-from-vim-to-emacs.html - GNU Emacs (home page)
https://www.gnu.org/software/emacs/ - GNU Emacs (texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?GnuEmacs - An Introduction To Using GDB Under Emacs
http://tedlab.mit.edu/~dr/gdbintro.html - An Introduction to Programming in Emacs Lisp
https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html - 27.6 Running Debuggers Under Emacs
https://www.gnu.org/software/emacs/manual/html_node/emacs/Debuggers.html - GdbMode
http://www.emacswiki.org/emacs/GdbMode - Emacs (Wikipedia)
https://en.wikipedia.org/wiki/Emacs - Emacs timeline
http://www.jwz.org/doc/emacs-timeline.html - Emacs Text Editors Family
http://texteditors.org/cgi-bin/wiki.pl?EmacsFamily - Vrapper aneb spojení možností Vimu a Eclipse
https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse/ - Vrapper aneb spojení možností Vimu a Eclipse (část 2: vyhledávání a nahrazování textu)
https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse-cast-2-vyhledavani-a-nahrazovani-textu/ - Emacs/Evil-mode – A basic reference to using evil mode in Emacs
http://www.aakarshnair.com/posts/emacs-evil-mode-cheatsheet - From Vim to Emacs+Evil chaotic migration guide
https://juanjoalvarez.net/es/detail/2014/sep/19/vim-emacsevil-chaotic-migration-guide/ - Introduction to evil-mode {video)
https://www.youtube.com/watch?v=PeVQwYUxYEg - EINE (Emacs Wiki)
http://www.emacswiki.org/emacs/EINE - EINE (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?EINE - ZWEI (Emacs Wiki)
http://www.emacswiki.org/emacs/ZWEI - ZWEI (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?ZWEI - Zmacs (Wikipedia)
https://en.wikipedia.org/wiki/Zmacs - Zmacs (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?Zmacs - TecoEmacs (Emacs Wiki)
http://www.emacswiki.org/emacs/TecoEmacs - Micro Emacs
http://www.emacswiki.org/emacs/MicroEmacs - Micro Emacs (Wikipedia)
https://en.wikipedia.org/wiki/MicroEMACS - EmacsHistory
http://www.emacswiki.org/emacs/EmacsHistory - Seznam editorů s ovládáním podobným Emacsu či kompatibilních s příkazy Emacsu
http://www.finseth.com/emacs.html - evil-numbers
https://github.com/cofi/evil-numbers - Debuggery a jejich nadstavby v Linuxu (1.část)
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2.část)
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
https://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Org mode
https://orgmode.org/ - The Org Manual
https://orgmode.org/manual/index.html - Kakoune (modální textový editor)
http://kakoune.org/ - Vim-style keybinding in Emacs/Evil-mode
https://gist.github.com/troyp/6b4c9e1c8670200c04c16036805773d8 - Emacs – jak začít
http://www.abclinuxu.cz/clanky/navody/emacs-jak-zacit - Programovací jazyk LISP a LISP machines
https://www.root.cz/clanky/programovaci-jazyk-lisp-a-lisp-machines/ - Evil-surround
https://github.com/emacs-evil/evil-surround - Spacemacs
http://spacemacs.org/ - Lisp: Common Lisp, Racket, Clojure, Emacs Lisp
http://hyperpolyglot.org/lisp - Common Lisp, Scheme, Clojure, And Elisp Compared
http://irreal.org/blog/?p=725 - Does Elisp Suck?
http://irreal.org/blog/?p=675 - Emacs pro mírně pokročilé (9): Elisp
https://www.root.cz/clanky/emacs-elisp/ - If I want to learn lisp, are emacs and elisp a good choice?
https://www.reddit.com/r/emacs/comments/2m141y/if_i_want_to_learn_lisp_are_emacs_and_elisp_a/ - Clojure(Script) Interactive Development Environment that Rocks!
https://github.com/clojure-emacs/cider - An Introduction to Emacs Lisp
https://harryrschwartz.com/2014/04/08/an-introduction-to-emacs-lisp.html - Emergency Elisp
http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html - Lambda calculus
https://en.wikipedia.org/wiki/Lambda_calculus - John McCarthy's original LISP paper from 1959
https://www.reddit.com/r/programming/comments/17lpz4/john_mccarthys_original_lisp_paper_from_1959/ - Micro Manual LISP
https://www.scribd.com/document/54050141/Micro-Manual-LISP - How Lisp Became God's Own Programming Language
https://twobithistory.org/2018/10/14/lisp.html - History of Lisp
http://jmc.stanford.edu/articles/lisp/lisp.pdf - The Roots of Lisp
http://languagelog.ldc.upenn.edu/myl/llog/jmc.pdf - The Racket Manifesto
http://felleisen.org/matthias/manifesto/ - MIT replaces Scheme with Python
https://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/ - Adventures in Advanced Symbolic Programming
http://groups.csail.mit.edu/mac/users/gjs/6.945/ - Why MIT Switched from Scheme to Python (2009)
https://news.ycombinator.com/item?id=14167453 - Starodávná stránka XLispu
http://www.xlisp.org/ - AutoLISP
https://en.wikipedia.org/wiki/AutoLISP - Seriál PicoLisp: minimalistický a výkonný interpret Lispu
https://www.root.cz/serialy/picolisp-minimalisticky-a-vykonny-interpret-lispu/ - Common Lisp
https://common-lisp.net/ - Getting Going with Common Lisp
https://cliki.net/Getting%20Started - Online Tutorial (Common Lisp)
https://cliki.net/online%20tutorial - Guile Emacs
https://www.emacswiki.org/emacs/GuileEmacs - Guile Emacs History
https://www.emacswiki.org/emacs/GuileEmacsHistory - Guile is a programming language
https://www.gnu.org/software/guile/ - MIT Scheme
http://groups.csail.mit.edu/mac/projects/scheme/ - SIOD: Scheme in One Defun
http://people.delphiforums.com/gjc//siod.html - CommonLispForEmacs
https://www.emacswiki.org/emacs/CommonLispForEmacs - Elisp: print, princ, prin1, format, message
http://ergoemacs.org/emacs/elisp_printing.html - Special Forms in Lisp
http://www.nhplace.com/kent/Papers/Special-Forms.html - Basic Building Blocks in LISP
https://www.tutorialspoint.com/lisp/lisp_basic_syntax.htm - Introduction to LISP – University of Pittsburgh
https://people.cs.pitt.edu/~milos/courses/cs2740/Lectures/LispTutorial.pdf - Why don't people use LISP
https://forums.freebsd.org/threads/why-dont-people-use-lisp.24572/ - Structured program theorem
https://en.wikipedia.org/wiki/Structured_program_theorem - Clojure: API Documentation
https://clojure.org/api/api - Tutorial for the Common Lisp Loop Macro
http://www.ai.sri.com/pkarp/loop.html - Common Lisp's Loop Macro Examples for Beginners
http://www.unixuser.org/~euske/doc/cl/loop.html - A modern list api for Emacs. No 'cl required.
https://github.com/magnars/dash.el - The LOOP Facility
http://www.lispworks.com/documentation/HyperSpec/Body/06_a.htm - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - Transient Data Structureshttp://clojure.org/transients
- Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Clojure (na Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (na Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - SICP (The Structure and Interpretation of Computer Programs)
http://mitpress.mit.edu/sicp/ - Pure function
http://en.wikipedia.org/wiki/Pure_function - Funkcionální programování
http://cs.wikipedia.org/wiki/Funkcionální_programování - Jazyky Hy a Clojure-py: moderní dialekty LISPu určené pro Python VM
https://www.root.cz/clanky/jazyky-hy-a-clojure-py-moderni-dialekty-lispu-urcene-pro-python-vm/ - Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/ - Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
https://www.root.cz/clanky/programovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/ - Stránka projektu Jython
http://www.jython.org/ - Jython (Wikipedia)
https://en.wikipedia.org/wiki/Jython - Scripting for the Java Platform (Wikipedia)
https://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - JSR 223: Scripting for the JavaTM Platform
https://jcp.org/en/jsr/detail?id=223 - List of JVM languages
https://en.wikipedia.org/wiki/List_of_JVM_languages - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354