Obsah
1. Jazyky Hy a Clojure-py: moderní dialekty LISPu určené pro Python VM
2. Python VM jakožto platforma pro další programovací jazyky
3. Programovací jazyk Hy umožňující elegantní propojení LISPu a Pythonu
5. Spuštění interpretu jazyka Hy a práce s interaktivní smyčkou REPL
7. Tečka-dvojice: základ pro tvorbu složitějších datových struktur v klasickém LISPu
8. Strukturované datové typy – kolekce a n-tice
9. Aritmetické operace a relační operátory
14. Instalace jazyka clojure-py
15. Spuštění interpretu a práce s interaktivní smyčkou REPL
16. Zpracování sekvencí a další základní mechanismy, na nichž je Clojure postaveno
17. Rozdílné chování oproti originálnímu jazyku Clojure
18. Repositář s demonstračními příklady
19. Odkazy na předchozí části tohoto seriálu o jazyku Clojure
1. Jazyky Hy a Clojure-py: moderní dialekty LISPu určené pro Python VM
V dnešním článku se (prozatím ovšem jen ve stručnosti) seznámíme s dvojicí dialektů LISPu určených pro běh ve virtuálním stroji Pythonu. Tyto dialekty jsou tedy navrženy takovým způsobem, aby se LISPovské programy v nich zapsané překládaly do bajtkódu Python VM, a to buď přímo nebo nepřímo přes mezikód představovaný Pythonem popř. AST (abstraktním syntaktickým stromem). Zaměříme se zejména na velmi pěkně navržený projekt Hy, který podporuje obousměrnou komunikaci s Pythonem a tudíž i se všemi knihovnami, debuggery atd. Popíšeme si i projekt Clojure-py; ten je však v současnosti neudržovaný, má několik chyb a prozatím běží pouze na Pythonu 2.
Spojení dialektu LISPu s virtuálním strojem Pythonu popř. přímo s programovacím jazykem Python (tzv.interop) není zajímavé jen z technologického hlediska, protože může mít i praktické využití. Ostatně LISP byl jazykem používaným pro vývoj AI, a to už v padesátých letech minulého století (z dnešního pohledu se ale mnohdy jednalo spíš o hledání slepých uliček). A dnes má v oblasti AI a strojového učení stejně výsadní postavení Python, takže se vlastně spojení LISP+Python jeví pragmaticky.
2. Python VM jakožto platforma pro další programovací jazyky
Fakt, že různé dialekty jazyků LISP a Scheme vznikají pro prakticky všechny moderní virtuální stroje (typicky pro JVM, VM JavaScriptu nebo VM Pythonu) vlastně není nic překvapivého. Musíme si totiž uvědomit, že praktická použitelnost programovacího jazyka je do značné míry určena i celým ekosystémem, který je programátorům k dispozici. A ekosystém Pythonu je dnes již velmi rozsáhlý a obsahuje kvalitní a v celém světě používané knihovny i celé frameworky, takže se může jednat o vhodný základ, na němž je možné postavit moderní varianty LISPu. Podobným způsobem ostatně vznikl i jazyk Clojure (což je taktéž dialekt LISPu, i když v mnoha ohledech vylepšený), který vlastně vůbec nemá svůj vlastní VM – původně Clojure vzniklo pro virtuální stroj Javy (JVM), další varianty posléze byly implementovány pro CLR (ClojureCLR) a ClojureScript, který je kompilovaný do JavaScriptu, takže může běžet buď v prohlížeči, v Node.js atd.
Pro Python VM je v současnosti k dispozici hned několik dialektů LISPu, Scheme či Clojure. Jedná se například o méně známé projekty Lizpop, Lispy či Lython, dále o SchemePy, Clojure-py o němž se zmíníme v dalším textu a především velmi zajímavým způsobem implementovaný programovací jazyk <a>Hy, kterému bude věnována větší část dnešního článku. Do této kategorie částečně spadá i jazyk Pixie, s nímž jsme se již na stránkách Roota seznámili.
3. Programovací jazyk Hy umožňující elegantní propojení LISPu a Pythonu
Programovací jazyk Hy je ve velké míře inspirován (poměrně populárním) jazykem Clojure, s nímž jsme se na stránkách Roota již mnohokrát setkali (viz odkazy). Ovšem zatímco interpret Clojure překládá všechny zapsané výrazy do bajtkódu JVM a teprve poté je spouští, pracuje Hy odlišně, protože kód generuje pomocí AST a dokonce dokáže zdrojový LISPovský kód transformovat do Pythonu a teprve poté ho spustit. To je výhodná vlastnost, protože umožňuje Hy integrovat například s debuggery atd. Překlad přes AST nebo Python podporuje jak Python 2.x, tak i Python 3.x. Další důležitou vlastností Hy možnost plné kooperace mezi kódem zapsaným přímo v tomto jazyku a Pythoním kódem, což znamená, že je možné použít všechny Pythonovské knihovny a frameworky (včetně Numpy, PyTorch, Flask atd.) a naopak – například mít napsanou aplikaci v Pythonu a pro manipulaci se symboly použít Hy (v tomto ohledu jsou homoikonické programovací jazyky s makry podle mého názoru mnohem lepší než samotný Python).
4. Instalace jazyka Hy
Instalace jazyka Hy je ve skutečnosti velmi snadná, protože je tento jazyk pochopitelně dostupný na PyPi, popř. je možné získat jeho zdrojové kódy na GitHubu. V dalším textu si popíšeme instalaci s využitím nástroje pip. Pro jednoduchost provedeme lokální instalaci pro právě přihlášeného uživatele, takže pip zavoláme s přepínačem –user. Používáme pip3, ovšem instalaci je v případě potřeby možné provést i pro Python 2:
$ pip3 install --user hy Downloading/unpacking hy Downloading hy-0.14.0-py2.py3-none-any.whl (68kB): 68kB downloaded Downloading/unpacking clint>=0.4 (from hy) Downloading clint-0.5.1.tar.gz Running setup.py (path:/tmp/pip_build_tester/clint/setup.py) egg_info for package clint warning: no files found matching '*' under directory 'docs' Downloading/unpacking astor>=0.6 (from hy) Downloading astor-0.6.2-py2.py3-none-any.whl Downloading/unpacking rply>=0.7.5 (from hy) Downloading rply-0.7.5-py2.py3-none-any.whl Downloading/unpacking args (from clint>=0.4->hy) Downloading args-0.1.0.tar.gz Running setup.py (path:/tmp/pip_build_tester/args/setup.py) egg_info for package args Downloading/unpacking appdirs (from rply>=0.7.5->hy) Downloading appdirs-1.4.3-py2.py3-none-any.whl Installing collected packages: hy, clint, astor, rply, args, appdirs Running setup.py install for clint warning: no files found matching '*' under directory 'docs' Running setup.py install for args Successfully installed hy clint astor rply args appdirs Cleaning up...
Po instalaci (pro právě přihlášeného uživatele) by se měly v adresáři ~/.local/bin objevit nové spustitelné soubory hy, hy2py atd.:
$ ls -l ~/.local/bin total 40 -rwxr-xr-x 1 tester tester 217 dub 24 21:21 flake8 -rwxr-xr-x 1 tester tester 218 kvě 17 21:27 hy -rwxr-xr-x 1 tester tester 224 kvě 17 21:27 hy2py -rwxr-xr-x 1 tester tester 224 kvě 17 21:27 hy2py3 -rwxr-xr-x 1 tester tester 218 kvě 17 21:27 hy3 -rwxr-xr-x 1 tester tester 220 kvě 17 21:27 hyc -rwxr-xr-x 1 tester tester 220 kvě 17 21:27 hyc3 -rwxr-xr-x 1 tester tester 215 dub 24 21:21 pycodestyle -rwxr-xr-x 1 tester tester 214 dub 24 21:21 pyflakes -rwxr-xr-x 1 tester tester 207 dub 24 21:21 radon
5. Spuštění interpretu jazyka Hy a práce s interaktivní smyčkou REPL
Interpret jazyka Hy se spouští velmi jednoduše: příkazem hy. Tento příkaz podporuje několik přepínačů, z nichž nejzajímavější je –spy, o němž se zmíníme v dalším textu:
$ hy --help usage: hy [-h | -i cmd | -c cmd | -m module | file | -] [arg] ... optional arguments: -h, --help show this help message and exit -c COMMAND program passed in as a string -m MOD module to run, passed in as a string -i ICOMMAND program passed in as a string, then stay in REPL --spy print equivalent Python code before executing --repl-output-fn REPL_OUTPUT_FN function for printing REPL output (e.g., hy.contrib.hy-repr.hy-repr) -v, --version show program's version number and exit --show-tracebacks show complete tracebacks for Hy exceptions file program read from script module module to execute as main - program read from stdin [arg] ... arguments passed to program in sys.argv[1:]
Zkusme si nyní interpret spustit a získat tak přístup k interaktivní smyčce REPL. Interpret se spustí prakticky okamžitě (na rozdíl od klasického Clojure založeného na JVM, kde je prodleva již patrnější). Výzva interpretru (prompt) se skládá ze znaků ⇒ (opět na rozdíl od Clojure bez uvedení výchozího jmenného prostoru):
$ hy hy 0.14.0 using CPython(default) 3.6.3 on Linux =>
V interaktivní smyčce je každý výraz po svém načtení (Read) ze standardního vstupu ihned vyhodnocen (Eval) a výsledná hodnota je následně vypsána do terminálu (Print) a interpret bude očekávat načtení dalšího výrazu (Loop). Některé výrazy jsou jednoduché, protože se vyhodnotí samy na sebe. Příkladem mohou být numerické hodnoty, pravdivostní hodnoty, řetězce atd:
=> 42 42 => 3.1415 3.1415 => 1+2j (1+2j) => 6j 6j => 4/7 Fraction(4, 7) => True True => False False => None
6. Základní datové typy
V této kapitole si popíšeme základní jednoduché datové typy. Především se jedná o pravdivostní hodnoty. A právě v tomto ohledu se Hy odlišuje od dalších dialektů LISPu, protože pro pravdivostní hodnoty používá True a False převzaté přímo z Pythonu. I prázdná hodnota se zapisuje jinak než v mnoha dalších interpretrech LISPu, protože se používá Pythonovské None. Rozdíly shrnuje následující tabulka:
Dialekt | Pravda | Nepravda | Prázdná hodnota |
---|---|---|---|
Common Lisp | t | nil | nil |
Scheme | #t | #f | '() |
Clojure | true | false | nil |
Hy | True | False | None |
Můžeme si to vyzkoušet:
=> None => True True => False False => nil Traceback (most recent call last): File "/home/tester/.local/lib/python3.4/site-packages/hy/importer.py", line 201, in hy_eval return eval(ast_compile(expr, "<eval>", "eval"), namespace) File "<eval>", line 1, in <module> NameError: name 'nil' is not defined => true Traceback (most recent call last): File "/home/tester/.local/lib/python3.4/site-packages/hy/importer.py", line 201, in hy_eval return eval(ast_compile(expr, "<eval>", "eval"), namespace) File "<eval>", line 1, in <module> NameError: name 'true' is not defined => false Traceback (most recent call last): File "/home/tester/.local/lib/python3.4/site-packages/hy/importer.py", line 201, in hy_eval return eval(ast_compile(expr, "<eval>", "eval"), namespace) File "<eval>", line 1, in <module> NameError: name 'false' is not defined
Programovací jazyk Hy rozlišuje mezi celými čísly, čísly s plovoucí řádovou čárkou, komplexními čísly a konečně zlomky. První tři numerické typy jsou převzaty z Pythonu a jsou s ním plně kompatibilní; zlomky jsou typické pro prakticky všechny LISPovské jazyky a jsou interně implementovány objektem. U numerických hodnot je možné používat prefixy 0×, 0o a 0b:
=> ; numerické hodnoty => ; celá čísla => 42 42 => 0x2a 42 => 0o52 42 => 0b00101010 42 => ; čísla s plovoucí řádovou čárkou => 3.14 3.14 => ; komplexní čísla => 1+2j (1+2j) => 3j 3j => ; zlomky => 2/3 Fraction(2, 3)
Řetězce se zapisují prakticky stejně jako v Pythonu, ovšem musí se použít uvozovky a nikoli apostrofy (ty mají odlišný význam). Podporovány jsou i prefixy pro Unicode řetězce (výchozí) a tzv. „raw“ řetězce (typicky používané při zápisu regulárních výrazů):
=> ; řetězce => "Hello world!" 'Hello world!' => u"Hello world!" 'Hello world!' => r"Hello world!" 'Hello world!'
Specialitou jsou tzv.„here dokumenty“, tj. víceřádkové řetězce s prakticky libovolným obsahem, jejichž zápis byl inspirován programovacím jazykem Lua. Tyto řetězce začínají znaky #[XXX[ a končí znaky ]XXX], přičemž za XXX je možné doplnit libovolně dlouhou (i prázdnou) sekvenci znaků, která se v řetězci v ideálním případě nebude vyskytovat:
; "here" řetězce #[[ ------- ( Lisp! ) ------- \ \ \_\_ _/_/ \ \__/ (oo)\_______ (__)\ )\/\ ||----w | || || ]] #[---[ ------- ( Lisp! ) ------- \ \ \_\_ _/_/ \ \__/ (oo)\_______ (__)\ )\/\ ||----w | || || ]---]
7. Tečka-dvojice: základ pro tvorbu složitějších datových struktur v klasickém LISPu
Programovací jazyk LISP je založen na zpracování seznamů. Jak jsou však seznamy uloženy v operační paměti počítače a jak s nimi interpretry tohoto jazyka pracují? Základní interní strukturou, která je však dostupná i programátorům aplikací v jazyce LISP, je takzvaná tečka-dvojice (dotted-pair). Tuto strukturu si můžeme představit jako dvojici ukazatelů, přičemž každý z těchto ukazatelů může obsahovat adresu atomu, adresu další tečka-dvojice nebo speciální hodnotu nil odpovídající v céčku hodnotě NULL či v Javě hodnotě null, tj. jedná se o speciální hodnotu, která interpretru říká, že daný ukazatel neobsahuje žádný odkaz. Tečka-dvojici lze v LISPovských programech zapisovat formou dvojice výrazů (takzvaných S-výrazů) oddělených tečkou, které jsou uzavřeny do kulatých závorek (i když je pravda, že se s tečka-dvojicemi v reálných programech příliš často nesetkáme, především z důvodu nepřehledného zápisu s velkým množstvím závorek).
Tečka dvojice jsou podporovány i v jazyku Hy, i když je prakticky nevyužijeme:
=> '(a . None) <HyCons ( HySymbol('a') . HySymbol('None'))> => '(a . b) <HyCons ( HySymbol('a') . HySymbol('b'))> => '(a . (b . (c . None))) <HyCons ( HySymbol('a') HySymbol('b') HySymbol('c') . HySymbol('None'))> => '( (a . b) (c . d)) HyExpression([ <HyCons ( HySymbol('a') . HySymbol('b'))>, <HyCons ( HySymbol('c') . HySymbol('d'))>])
; seznam zapsaný pomocí tečka-dvojic (1.(2.(3.(4.(5.nil))))) ; běžný způsob zápisu seznamu (1 2 3 4 5) ; interní struktura seznamu v paměti ; . ; / \ ; 1 . ; / \ ; 2 . ; / \ ; 3 . ; / \ ; 4 . ; / \ ; 5 nil
Poznamenejme, že další struktury vytvořené pomocí rekurzivně zanořených tečka-dvojic není možné převést na běžné seznamy. Například jednoduchý binární strom se třemi úrovněmi a čtyřmi listy lze reprezentovat buď pomocí tečka-dvojic (v paměti se vytvoří skutečná obdoba binárního stromu), popř. je možné tuto datovou strukturu „simulovat “pomocí seznamů (ovšem v tomto případě bude paměťová náročnost vyšší kvůli nutnosti ukončení všech podseznamů tečka dvojicí obsahující ve svém druhém ukazateli hodnotu nil):
; binární strom se třemi úrovněmi a čtyřmi listy vytvořený pomocí tečka dvojic ((A.B).(C.D)) ; interní podoba této struktury v operační paměti: ; . ; / \ ; . . ; / \ / \ ; A B C D ; binární strom vytvořený pomocí LISPovských seznamů ((A B) (C D)) ; interní podoba této struktury v operační paměti: ; . ; / \ ; / \ ; / \ ; / \ ; . . ; / \ / \ ; A . . nil ; / \ / \ ; B nil C . ; / \ ; D nil
8. Strukturované datové typy – kolekce a n-tice
Programovací jazyk Hy podporuje všechny základní strukturované datové typy, které známe z jazyka Clojure. Jedná se o seznamy, vektory, mapy i množiny:
# | Typ kolekce | Zápis konstruktoru |
---|---|---|
1 | Seznam | '(prvky) |
2 | Vektor | [prvky] |
3 | Mapa | {dvojice klíč-hodnota} |
4 | Množina | #{unikátní prvky} |
Povšimněte si, že u seznamů je nutné použít znak ' (quote), jinak by se zápis seznamu chápal jako volání funkce nebo vyhodnocení makra.
Do této skupiny datových typů můžeme zařadit i n-tici (tuple), která se zapisuje takto:
(, prvky)
Jedná se o specialitu jazyka Hy a v dalších dialektech LISPu ji nenajdeme, i když se jedná o velmi užitečnou strukturu – viz též Lisp's Mysterious Tuple Problem.
Příklady:
; pomocné proměnné (setv positionx 1) (setv positionY 2) (setv positionZ 3) ; Seznamy ; prázdný seznam '() ; seznam čísel '(1 2 3 4) ; seznam řetězců '("prvni" "druhy" "treti") ; seznam "keywords" '(:prvni :druhy :treti) ; seznam s proměnnými '(positionX positionY positionZ) ; vnořené seznamy '( '(:x :y) '(:z :w) ) ; Vektory ; prázdný vektor [] ; vektor čísel [1 2 3 4] ; vektor řetězců ["prvni" "druhy" "treti"] ; vektor "keywords" [:prvni :druhy :treti] ; vektor proměnných [positionX positionY positionZ] ; n-tice ; prázdná n-tice (,) ; n-tice s čísly (, 1 2 3 4) ; n-tice řetězců (, "prvni" "druhy" "treti") ; n-tice "keywords" (, :prvni :druhy :treti) ; Mapa ; prázdná mapa {} ; mapování typu string-string {"prvni" "first" "druhy" "second" "treti" "third"} ; mapa s vyhodnocením proměnných {"X" positionX "y" positionY "z" positionZ} ; Množina #{"prvni" "druhy" "treti"}
9. Aritmetické operace a relační operátory
Zatímco v naprosté většině „mainstreamových“ programovacích jazyků, jakými jsou například Céčko, Java, JavaScript či Python, se aritmetické a logické výrazy zapisují v takzvané infixové notaci, při níž jsou binární operátory zapisovány mezi dvojici operandů, autor jazyka Hy (resp. přesněji řečeno již tvůrci LISPu) se od tohoto způsobu zápisu distancovali – namísto toho jsou v Hy i LISPu všechny základní aritmetické i logické (a samozřejmě též relační) operace zapisovány jako volání funkcí či speciálních forem, tj. vždy v prefixové podobě. Důvodů, proč byla zvolena tato forma zápisu výrazů, je více. Prvním důvodem je fakt, že syntaxe LISPu byla původně navrhována s ohledem na to, že později dojde k její změně, tj. samotná syntaxe nebyla pro tvůrce tohoto programovacího jazyka tak prioritní jako jeho sémantika (paradoxní přitom je, že se nakonec syntaxe LISPu nezměnila, protože takzvané M-výrazy se nedočkaly většího rozšíření, podobně jako další snahy o úpravu syntaxe LISPu tak, aby se eliminovalo množství závorek či právě prefixový zápis aritmetických výrazů).
Druhý důvod spočíval v tom, že zavedení infixových operátorů by do jazyka zavádělo zbytečné další komplikace: musely by se například řešit a přesně specifikovat priority operací (a u některých operací i jejich asociativita, viz například ** v Pythonu), se zapsanými výrazy by se složitěji prováděly různé symbolické manipulace (integrace, derivace, zjednodušování výrazů), infixové operátory by nebylo možné předávat jako parametry do jiných funkcí (apply, reduce) atd. Vzhledem k tomu, že aritmetické operátory jsou zapisovány jako volání funkcí, musí se znak či jméno příslušného operátoru uvádět ve vyhodnocovaném seznamu na prvním místě, podobně jako jméno jakékoli jiné funkce. Všechny dílčí podvýrazy se samozřejmě vyhodnocují dříve než celý výraz. Většina aritmetických funkcí není omezena pouze na dva parametry, což znamená, že je například možné zavoláním jedné funkce nazvané + sečíst i více než dvě numerické hodnoty.
Podívejme se na okomentované příklady:
; Test překladu aritmetických výrazů ; operace rozdílu - druhý argument funkce je odečten od prvního (- 1 2) ; součet řady čísel (+ 1 2 3 4 5 6 7 8 9 10) ; níže uvedený výraz v infixové notaci odpovídá: 1-2-3-4-5....-10: (- 1 2 3 4 5 6 7 8 9 10) ; POZOR - závorky v LISPu nemají mnoho společného ; s vyjádřením priority aritmetických operací ; (nelze je použít tak volně jako například v céčku) (* (+ 1 2) (+ 3 4)) (+ (* 1 2) (* 3 4)) ; Hy umí, podobně jako některé implementace LISPu, ; pracovat se zlomky, tj. snaží se racionální ; čísla vyjádřit formou zlomku (ideální jazyk do škol) ; Hy se chová jinak - se zlomky sice pracuje, ale / přeloží do operátoru / (/ 1 2) (/ 1 2 3) ; zkusíme výpočet složitějšího zlomku (/ (+ 1 2) (+ 3 4)) ; dělení modulo (% 10 3) ; 2^10 (** 2 10) ; neracionální (reálná) čísla se vypisují tak, jak to ; známe z ostatních programovacích jazyků (samozřejmě ; v případě speciálních požadavků programátora lze použít ; různé formátovací funkce na úpravu výstupu) (* 0.3 (/ (+ 1 2) (+ 3 4))) ; namísto numerických hodnot lze použít i proměnné (setv variableA 1) (setv variableB 2) (setv variableC 3) (setv variableD 4) (+ variableA variableB variableC variableD) (* (+ variableA variableB) (+ variableC variableD)) (+ (* variableA variableB) (* variableC variableD)) (/ (+ variableA variableB) (+ variableC variableD)) (+= variableA 10) (*= variableB 2)
Programovací jazyk Hy obsahuje i úplnou sadu relačních operátorů, které v závislosti na hodnotách předaných parametrů (operandů) vrací hodnotu True (pravda) či False (nepravda):
; Test překladu relačních výrazů ; porovnání dvou číselných hodnot ; relace "menší než" (< 1 2) ; relace "větší než" (> 1 2) ; relace "menší nebo rovno" (<= 1 2) ; relace "větší nebo rovno" (>= 1 2) ; porovnání dvou výrazů na ekvivalenci (= 1 2) (= 1 1) ; podvýrazy se nejprve vyhodnotí a posléze se porovnají ; vyhodnocené výsledky (v tomto případě dva atomy) (= (+ 1 1) (/ 4 2) (** 2 10))
Namísto numerických hodnot lze použít i proměnné:
(setv variableA 1) (setv variableB 2) (setv variableC 3) (setv variableD 4) (< variableA variableB) (> variableA variableB) (<= variableA variableB) (>= variableA variableB) (= variableA variableB) (= variableA variableA) (= (+ variableA variableA) (/ variableC variableD)) (or (= variableA 10) (= variableB 20) (> variableC 0)) (and (> variableA 0) (< variableA 100))
10. Anonymní funkce
Pro vytvoření nové bezejmenné (tj. anonymní) funkce se používá speciální forma nazvaná fn, které se v tom nejjednodušším případě předá vektor obsahující jména parametrů, za nímž je uveden seznam, který představuje tělo funkce (znalci LISPu patrně znají formu lambda, která má podobný význam, ale zde být použita nemůže, protože se jedná o klíčové slovo Pythonu). Samozřejmě, že v těle funkce je možné použít symbolická jména jejích parametrů a návratovou hodnotou funkce je hodnota získaná vyhodnocením těla funkce. Speciální forma fn při použití ve smyčce REPL vypíše řetězec, který reprezentuje interní identifikátor funkce – jinými slovy na tento řetězec můžeme v naprosté většině případů zapomenout, protože se s ním přímo nepracuje. Ukažme si tedy způsob deklarace funkce se dvěma parametry pojmenovanými x a y, která vypočítá a vrátí součet těchto parametrů:
; anonymní funkce (fn [x y] (+ x y))
Co se vlastně stalo? Vytvořili jsme novou funkci, která však nebyla přiřazena k žádnému symbolu (tj. nebyla „pojmenována“) ani jsme tuto funkci nikde nezavolali. Výše uvedený zápis je tedy prakticky stejně užitečný jako prosté zapsání jakékoli hodnoty nebo symbolu na vstup smyčky REPL. Pokud by se funkce měla zavolat, lze použít nám již známý zápis ve tvaru seznamu – již víme, že prvním parametrem vyhodnocovaného seznamu (není před ním apostrof!) je funkce a dalšími prvky pak parametry této funkce:
((fn [x y] (* x y)) 6 7) 42
Sice je pěkné, že jsme dokázali funkci zavolat s předáním parametrů, ovšem mnohdy (ne vždy!) je nutné funkci „pojmenovat“, přesněji řečeno ji přiřadit k symbolu. My vlastně již víme, jak se to dělá, protože funkce jsou hodnotami a pro přiřazení symbolu k hodnotě se používá speciální forma setv. Tudíž následující zápis je sice zdlouhavý, ale zcela korektní:
(setv multiply (fn [x y] (* x y))) #'user/multiply
Předchozím příkazem jsme vytvořili novou funkci a navázali ji na symbol, tudíž došlo k jejímu pojmenování. Nyní je již možné funkci zavolat s využitím navázaného symbolu. Samozřejmě se zde opět využívá nám již známý zápis ve tvaru seznamu:
(multiply 6 7) 42
11. Pojmenované funkce
Vzhledem k tomu, že se uživatelské funkce v reálných programech vytváří a současně i pojmenovávají velmi často, vznikla potřeba nahradit zápis (sevv název (fn parametry (tělo))) něčím kratším, ideálně i s použitím menšího množství závorek. Pro tyto účely vzniklo makro se jménem defn, které se až na malé detaily podobá LISPovskému zápisu defun. Při použití makra defn se v tom nejjednodušším případě předávají tři parametry: název nově vytvářené funkce, vektor obsahující jména parametrů funkce a konečně seznam představující tělo této funkce. Naši funkci multiply tedy můžeme vytvořit a současně i pojmenovat následujícím způsobem:
(defn multiply [x y] (* x y))
A ihned ji můžeme použít:
(multiply 6 7) 42
Následují příklady dalších funkcí:
; Test překladu deklarace funkcí ; anonymní funkce (fn [x y] (+ x y)) ; funkce navázaná na symbol == pojmenovaná funkce (defn add [x y] (+ x y)) ; lokální symboly (proměnné) (defn add-abs [x y] (setv abs-x (if (< x 0) (- x) x)) (setv abs-y (if (< y 0) (- y) y)) (+ abs-x abs-y)) (defn inc [num] (+ num 1)) (print (add-abs -10 -20))
12. Rekurze a tail rekurze
Zcela typickým příkladem rekurzivní funkce je funkce pro výpočet faktoriálu, jejíž jednoduchá varianta (neochráněná před všemi typy vstupů) může vypadat takto:
; rekurzivní výpočet faktoriálu (defn factorial [n] (if (<= n 1) 1 (* n (factorial (- n 1)))))
Otestování je snadné:
(print (factorial 10)) 3628800 (for [n (range 1 11)] (print n (factorial n))) 1 1 2 2 3 6 4 24 5 120 6 720 7 5040 8 40320 9 362880 10 3628800
Přílišnému nadšení nad tím, jak jednoduše nyní můžeme počítat faktoriál z libovolně velkého čísla, však nepodléhejme, protože například již pro 10000! dojde k nepříjemnému překvapení:
(factorial 10000) RuntimeError: maximum recursion depth exceeded in comparison
Důvod, proč předchozí volání funkce factorial skončilo s chybou, spočívá v tom, že došlo k přeplnění zásobníku při rekurzivním volání. Na zásobník se totiž musí ukládat parametry předávané volané funkci a taktéž body návratu (zjednodušeně řečeno návratové adresy). Aby k přetečení zásobníku nedocházelo, můžeme naši funkci upravit tak, aby se využívalo takzvané tail rekurze. Velmi zjednodušeně řečeno je tail rekurze použita tehdy, pokud je posledním příkazem nějaké funkce příkaz pro rekurzivní volání té samé funkce. V tomto případě se nemusí na zásobník nic ukládat a namísto toho se prostě provede skok. V jazyku Hy, ale i v Clojure, se musí tail rekurze zapsat explicitně, což má své přednosti i zápory (podle mě převažují přednosti, protože již ze zápisu programu je zcela zřejmé, kdy k tail rekurzi skutečně dojde):
; rekurzivní výpočet faktoriálu - TCO (require [hy.contrib.loop [loop]]) (defn factorial [n] (loop [[cnt n] [acc 1]] (if (zero? cnt) acc (recur (dec cnt) (* acc cnt))))) (print (factorial 10)) (for [n (range 1 11)] (print n (factorial n)))
Oba dva příklady uvedené výše jsou však spíše školní. V praxi se faktoriál dá vypočítat s využitím funkce vyššího řádu reduce, která postupně aplikuje nějakou specifikovanou funkci se dvěma parametry (zde konkrétně funkci *) na hodnotu získanou ze sekvence a na hodnotu automaticky vytvořeného akumulátoru. Jen si musíme dát pozor na to, aby první prvek sekvence obsahoval jedničku a nikoli nulu:
; nerekurzivní výpočet faktoriálu (defn factorial [n] (if (neg? n) (raise (ValueError "natural number expected")) (reduce * (range 1 (inc n))))) (print (factorial 10)) (for [n (range 1 11)] (print n (factorial n))) (print (factorial -10))
13. Jazyk Clojure-py
Druhým LISPovským programovacím jazykem, s nímž se dnes ve stručnosti seznámíme, je jazyk nazvaný Clojure-py. Soudě podle názvu by se mělo jednat o další variantu jazyka Clojure, ovšem projekt Clojure-py je v současnosti v dosti nestabilním stavu, takže například dochází k pádu VM (přesněji řečeno k pádu interpretru) atd. I z tohoto důvodu si o Clojure-py řekneme jen základní informace, protože pro praktické nasazení je mnohem lepší a především méně riskantní použít Hy.
14. Instalace jazyka clojure-py
Instalace interpretru Clojure-py může opět proběhnou s využitím nástroje pip, ovšem musíte si ověřit, že používáte pip pro Python 2. Instalaci provedeme pouze pro přihlášeného uživatele, ať si zbytečně nerozbijeme systém nestabilním balíčkem:
$ pip install --user clojure_py Collecting clojure_py Using cached https://files.pythonhosted.org/packages/e9/d1/77ca45d549ee5879c615eb4431db4c94b4c90cb2be6705d652efcc08e02e/clojure_py-0.2.4.tar.gz Installing collected packages: clojure-py Running setup.py install for clojure-py ... done Successfully installed clojure-py-0.2.4 You are using pip version 9.0.1, however version 10.0.1 is available. You should consider upgrading via the 'pip install --upgrade pip' command.
O tom, že jazyk Clojure-py již není aktivně vyvíjen, svědčí i chyba, která vznikne při snaze o instalaci tohoto balíčku pomocí pip3, tedy pro Python 3:
$ pip3 install --user clojure_py Collecting clojure_py Downloading https://files.pythonhosted.org/packages/e9/d1/77ca45d549ee5879c615eb4431db4c94b4c90cb2be6705d652efcc08e02e/clojure_py-0.2.4.tar.gz (100kB) 100% |████████████████████████████████| 102kB 995kB/s Complete output from command python setup.py egg_info: Traceback (most recent call last): File "<string>", line 1, in <module> File "/tmp/pip-build-asotz3jt/clojure-py/setup.py", line 10, in <module> from clojure.main import VERSION File "/tmp/pip-build-asotz3jt/clojure-py/clojure/__init__.py", line 2, in <module> import clojure.main File "/tmp/pip-build-asotz3jt/clojure-py/clojure/main.py", line 68 print s, filename ^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(print s, filename)? ---------------------------------------- Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-asotz3jt/clojure-py/
15. Spuštění interpretu a práce s interaktivní smyčkou REPL
Po (doufejme že úspěšné) instalaci si můžeme spustit interpret, resp. přesněji řečeno interaktivní smyčku REPL:
$ clojurepy clojure-py 0.2.4 Python 2.7.14 (default, Dec 11 2017, 16:08:01) [GCC 7.2.1 20170915 (Red Hat 7.2.1-2)] user=>
A můžeme začít psát výrazy, které mají prakticky stejnou sémantiku, jako je tomu v Clojure:
user=> (println 42) 42 nil ; sekvence user=> (def x (range 10)) #'user/x user=> x (0 1 2 3 4 5 6 7 8 9) ; nová sekvence s přidaným prvkem user=> (conj x 10) (10 0 1 2 3 4 5 6 7 8 9)
K dispozici je i nápověda k jednotlivým funkcím a makrům:
user=> (doc conj) conj[oin]. Returns a new collection with the xs 'added'. (conj nil item) returns (item). The 'addition' may happen at different 'places' depending on the concrete type. nil
Generátorová notace seznamu:
user=> (for [n (range 0 11)] (factorial n)) (1 1 2 6 24 120 720 5040 40320 362880 3628800)
Deklarace funkce pro výpočet faktoriálu:
user=> (defn factorial [n] (reduce * (range 1 (inc n)))) #'user/factorial
Otestování funkce pro výpočet faktoriálu:
user=> (factorial 42) 1405006117752879898543142606244511569936384000000000
16. Zpracování sekvencí a další základní mechanismy, na nichž je Clojure postaveno
Jen ve stručnosti si ukažme práci se sekvencemi a kolekcemi, které tvoří základní heterogenní datové struktury Clojure:
user=> [1 2 3] [1 2 3] user=> '(1 2 3) (1 2 3) user=> {:name "Eda" :surname "Wasserfall"} {:surname "Wasserfall", :name "Eda"}
user=> (def seznam '(1 2 3 'a 'b "hello")) #'user/seznam user=> (def vektor [1 2 3 'a 'b "hello"]) #'user/vektor user=> (def mnozina #{1 2 3}) #'user/mnozina user=> (def vektor [1 2 3 'a 'b "hello"]) #'user/vektor user=> (def mnozina #{1 2 3}) #'user/mnozina user=> (empty? [1 2 3]) false user=> (count [1 2 3]) 3 user=> (def seznam '(1 2 3)) #'user/seznam user=> (conj seznam 100) (100 1 2 3) user=> (def vektor [1 2 3]) #'user/vektor user=> (conj vektor 100) [1 2 3 100] user=> (pop [1 2 3]) [1 2] user=> (nth [1 2 3] 1) 2
Použití funkce vyššího řádu apply:
user => (doseq [n (range 1 11)] (println n (apply * (range 1 (inc n))))) 1 1 2 2 3 6 4 24 5 120 6 720 7 5040 8 40320 9 362880 10 3628800 nil
Použití funkce vyššího řádu reduce:
user => (doseq [n (range 1 11)] (println n (reduce * (range 1 (inc n))))) 1 1 2 2 3 6 4 24 5 120 6 720 7 5040 8 40320 9 362880 10 3628800 nil
Použití funkce vyššího řádu map:
user=> (def a (range 1 11)) #'user/a user=> (->> a (map #(* % 2)) (reduce +)) 110
17. Rozdílné chování oproti originálnímu jazyku Clojure
Některé funkce a datové typy Clojure nejsou v Clojure-py prozatím implementovány, takže relativně často dochází k chybám. Příkladem může být pokus o práci se zlomky:
user=> 3/7 Compiling 3/7 Traceback (most recent call last): File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 117, in run_repl out = execute(line) File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 67, in execute res = comp.compile(s) File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1342, in compile + str(type(itm)), None) CompilerException: Compiler Exception don't know how to compile <class 'fractions.Fraction'>
Co je však mnohem horší – nefungují ani některé funkce pro zpracování sekvencí:
user=> (filter even? [1 2 3 4]) (2 4) user=> (filter even? (map inc (range 10))) (Traceback (most recent call last): File "/home/tester/.local/bin/clojurepy", line 11, in <module> load_entry_point('clojure-py==0.2.4', 'console_scripts', 'clojurepy')() File "/home/tester/.local/lib/python2.7/site-packages/clojure/main.py", line 91, in main clojure.repl.run_repl(comp) File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 123, in run_repl RT.printTo(out) File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/rt.py", line 328, in printTo protocols.writeAsReplString(obj, writer) File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/protocol.py", line 46, in __call__ return getattr(x, self.attrname)(*args) File "/home/tester/.local/lib/python2.7/site-packages/clojure/core.clj", line 796, in clojure.core/LazySeq_writeAsReplString (loop [s (.seq self)] File "/home/tester/.local/lib/python2.7/site-packages/clojure/core.clj", line 0, in clojure.core/LazySeq_seq (loop [s (.seq self)] File "/home/tester/.local/lib/python2.7/site-packages/clojure/core.clj", line 0, in clojure.core/LazySeq_sval (loop [s (.seq self)] File "/home/tester/.local/lib/python2.7/site-packages/clojure/core.clj", line 1812, in clojure.core/filter304_auto_ (when (pred (.nth c i)) AttributeError: 'ArrayChunk' object has no attribute 'nth'
Některé základní funkce z Clojure v Clojure-py nenajdeme:
user=> (into {} (for [x (range 1 10)] [x (* x x)])) Compiling into Compiling (into, {}, (for, [x, (range, 1, 10)], [x, (*, x, x)])) Traceback (most recent call last): File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 117, in run_repl out = execute(line) File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 67, in execute res = comp.compile(s) File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1315, in compile c.extend(self.compileForm(itm)) File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1226, in compileForm c = self.compile(form.first()) File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1313, in compile c.extend(self.compileSymbol(itm)) File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1284, in compileSymbol return self.compileAccessList(sym) File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1241, in compileAccessList code = self.getAccessCode(sym) File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1252, in getAccessCode " reference " + str(self.getNamesString(False)), None) CompilerException: Compiler Exception could not resolve 'into', 'into' not found in user reference fn_1082
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes zmíněných demonstračních příkladů byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/lisps-for-python-vm. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, stále doslova několik kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
19. Odkazy na předchozí části tohoto seriálu o jazyku Clojure
- Clojure 1: Úvod
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/ - Clojure 2: Symboly, kolekce atd.
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/ - Clojure 3: Funkcionální programování
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/ - Clojure 4: Kolekce, sekvence a lazy sekvence
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/ - Clojure 5: Sekvence, lazy sekvence a paralelní programy
http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/ - Clojure 6: Podpora pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/ - Clojure 7: Další funkce pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/ - Clojure 8: Identity, stavy, neměnné hodnoty a reference
http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/ - Clojure 9: Validátory, pozorovatelé a kooperace s Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/ - Clojure 10: Kooperace mezi Clojure a Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/ - Clojure 11: Generátorová notace seznamu/list comprehension
http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/ - Clojure 12: Překlad programů z Clojure do bajtkódu JVM I:
http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/ - Clojure 13: Překlad programů z Clojure do bajtkódu JVM II:
http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/ - Clojure 14: Základy práce se systémem maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/ - Clojure 15: Tvorba uživatelských maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/ - Clojure 16: Složitější uživatelská makra
http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/ - Clojure 17: Využití standardních maker v praxi
http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/ - Clojure 18: Základní techniky optimalizace aplikací
http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/ - Clojure 19: Vývojová prostředí pro Clojure
http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/ - Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/ - Clojure 21: ClojureScript aneb překlad Clojure do JS
http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/ - Leiningen: nástroj pro správu projektů napsaných v Clojure
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/ - Programovací jazyk Clojure a databáze (1.část)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/ - Pluginy pro Leiningen
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/ - Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk/ - Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-2/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (2)
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-2/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (3)
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-3/ - Programovací jazyk Clojure a práce s Gitem
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/ - Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (dokončení)
http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-dokonceni/ - Programovací jazyk Clojure a práce s Gitem (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/ - Programovací jazyk Clojure – triky při práci s řetězci
http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/ - Programovací jazyk Clojure – triky při práci s kolekcemi
http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/ - Programovací jazyk Clojure – práce s mapami a množinami
http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/ - Programovací jazyk Clojure – základy zpracování XML
http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/ - Programovací jazyk Clojure – testování s využitím knihovny Expectations
http://www.root.cz/clanky/programovaci-jazyk-clojure-testovani-s-vyuzitim-knihovny-expectations/ - Programovací jazyk Clojure – některé užitečné triky použitelné (nejenom) v testech
http://www.root.cz/clanky/programovaci-jazyk-clojure-nektere-uzitecne-triky-pouzitelne-nejenom-v-testech/ - Enlive – výkonný šablonovací systém pro jazyk Clojure
http://www.root.cz/clanky/enlive-vykonny-sablonovaci-system-pro-jazyk-clojure/ - Nástroj Leiningen a programovací jazyk Clojure: tvorba vlastních knihoven pro veřejný repositář Clojars
http://www.root.cz/clanky/nastroj-leiningen-a-programovaci-jazyk-clojure-tvorba-vlastnich-knihoven-pro-verejny-repositar-clojars/ - Novinky v Clojure verze 1.8.0
http://www.root.cz/clanky/novinky-v-clojure-verze-1–8–0/ - Asynchronní programování v Clojure s využitím knihovny core.async
http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async/ - Asynchronní programování v Clojure s využitím knihovny core.async (pokračování)
http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-pokracovani/ - Asynchronní programování v Clojure s využitím knihovny core.async (dokončení)
http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-dokonceni/ - Vytváříme IRC bota v programovacím jazyce Clojure
http://www.root.cz/clanky/vytvarime-irc-bota-v-programovacim-jazyce-clojure/ - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Multimetody v Clojure aneb polymorfismus bez použití OOP
https://www.root.cz/clanky/multimetody-v-clojure-aneb-polymorfismus-bez-pouziti-oop/ - Práce s externími Java archivy v programovacím jazyku Clojure
https://www.root.cz/clanky/prace-s-externimi-java-archivy-v-programovacim-jazyku-clojure/ - 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/ - Novinky v Clojure verze 1.9.0
https://www.root.cz/clanky/novinky-v-clojure-verze-1–9–0/ - Validace dat s využitím knihovny spec v Clojure 1.9.0
https://www.root.cz/clanky/validace-dat-s-vyuzitim-knihovny-spec-v-clojure-1–9–0/ - Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure
https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure/ - Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure (2)
https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure-2/
20. Odkazy na Internetu
- Python becomes a platform
https://khinsen.wordpress.com/2012/03/15/python-becomes-a-platform/ - Python becomes a platform. Thoughts on the release of clojure-py
https://news.ycombinator.com/item?id=3708974 - SchemePy
https://pypi.org/project/SchemePy/ - 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 - Clojure home page
http://clojure.org/ - Clojure Sequences
http://clojure.org/sequences - Clojure Data Structures
http://clojure.org/data_structures - Clojure
https://en.wikipedia.org/wiki/Clojure - Clojars:
https://clojars.org/ - Seznam knihoven na Clojars:
https://clojars.org/projects - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDoc (rozcestník s dokumentací jazyka Clojure)
http://clojuredocs.org/ - 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í - Čistě funkcionální (datové struktury, jazyky, programování)
http://cs.wikipedia.org/wiki/Čistě_funkcionální - 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 - Threading macro (dokumentace k jazyku Clojure)
https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/-> - Understanding the Clojure → macro
http://blog.fogus.me/2009/09/04/understanding-the-clojure-macro/ - Emacs LISP
https://www.root.cz/clanky/historie-vyvoje-textovych-editoru-eine-zwei-emacs/#k08 - Programovací jazyk LISP a LISP machines
https://www.root.cz/clanky/programovaci-jazyk-lisp-a-lisp-machines/ - Speciální formy, lambda výrazy a makra v programovacím jazyku LISP
https://www.root.cz/clanky/specialni-formy-lambda-vyrazy-a-makra-v-programovacim-jazyku-lisp/ - Programovací jazyky používané (nejen) v SSSR (část 3 – LISP)
https://www.root.cz/clanky/programovaci-jazyky-pouzivane-nejen-v-nbsp-sssr-cast-3-ndash-lisp/