Hlavní navigace

Možnosti nabízené jazykem Hy: moderním dialektem LISPu určeným pro Python VM

Pavel Tišnovský

Ve druhé části článku o jazyku Hy si ukážeme jak použití některých typicky LISPovských technik, tak i možnosti velmi dobře navržené kooperace mezi Hy a Pythonem. Také si ukážeme, jak lze Hy přeložit do Pythonu 2 i 3.

Doba čtení: 31 minut

Obsah

1. Možnosti nabízené jazykem Hy: moderním dialektem LISPu určeným pro Python VM

2. Zpracování kolekcí – vektorů a slovníků

3. Funkce a makra určená pro práci s vektory

4. Modifikace obsahu vektoru s využitím funkcí cut, del, assoc a metody append

5. Funkce a makra určená pro práci se slovníky

6. Generátorová notace slovníku a použití funkce zip pro vytvoření slovníku

7. Pravidla pro pojmenování funkcí v jazyku Hy

8. Volání funkcí a metod definovaných v Pythonu

9. Volání funkcí definovaných v jazyku Hy z Pythonu

10. Použití maker v jazyku Hy

11. Smyčka REPL (Read-Eval-Print-Loop) a systém maker v tradičních LISPech

12. Využití eval

13. Makra „quote“ a „syntax-quote“

14. Praktické použití – jednoduchá makra

15. Transpřeklad programů z jazyka Hy do Pythonu

16. Složitější příklad – vygenerování SVG souboru s logem

17. Porovnání SVG generátoru s podobným příkladem naprogramovaným v Clojure a Pixie

18. Repositář s demonstračními příklady

19. Odkazy na předchozí části seriálu o jazyku Clojure

20. Odkazy na Internetu

1. Možnosti nabízené jazykem Hy: moderním dialektem LISPu určeným pro Python VM

V předchozím článku jsme se ve stručnosti seznámili se dvěma dialekty programovacího jazyka LISP určenými pro běh ve virtuálním stroji Pythonu (přesněji řečeno CPythonu, protože Python je dnes možné provozovat i nad JVM či CLR). Připomeňme si, že se jedná o projekty Hy a Clojure-py, přičemž první zmíněný projekt Hy je aktivně vyvíjený, používaný v praxi a především umožňuje velmi dobrou kooperaci s Pythonem (dokonce do větší míry, než je zajištěna kooperace mezi Clojure a Javou). Dnes se seznámíme s dalšími možnostmi nabízenými jazykem Hy. Nejprve si podrobněji ukážeme, jakým způsobem se pracuje s kolekcemi, dále si ukážeme možnosti makrosystému jazyka Hy a konečně si řekneme, jakým způsobem je možné z jazyka Hy volat funkce a metody naprogramované v Pythonu a naopak – jak volat funkce vytvořené v jazyku Hy z Pythonu. Ostatně právě tato oboustranná interoperabilita dělá z Hy prakticky použitelný projekt.

2. Zpracování kolekcí – vektorů a slovníků

V úvodní části dnešního článku se ještě jednou vrátíme k problematice práce s kolekcemi (tj. zejména s vektory a se slovníky), protože to je jedna z oblastí, v níž se programovací jazyk Hy odlišuje od Clojure. Důvody pro všechny dále zmíněné rozdíly jsou ve skutečnosti velmi pragmatické – vzhledem k tomu, že jsou vektory představované Pythonovskými seznamy, není například možné zachovat neměnnost (immutability), což je jeden z hlavních konceptů, na nichž je postaven programovací jazyk Clojure. Většina základních funkcí navíc vrací přímo vektor a nikoli tzv. línou sekvenci (lazy sequence) tak, jak je tomu v Clojure.

Na začátek si připomeňme, že Hy rozlišuje mezi seznamem (list), což je datová struktura podobná klasickému LISPovskému seznamu tvořenému tečka-dvojicemi a mezi vektorem (vector), který odpovídá Pythonovskému seznamu, jehož obsah i tvar (shape) je měnitelný. Vektor se vytvoří následovně:

=> ; vektory nejsou neměnné (immutable) tak jako v Clojure!
=> (setv vektor [1 2 3 4])

Takto vytvořený vektor je možné modifikovat – měnit hodnotu jeho prvků, měnit jeho tvar (shape), tj. počet prvků atd.

3. Funkce a makra určená pro práci s vektory

V dalších příkladech si ukážeme základní funkce určené pro práci s vektory. Jedná se v první řadě o funkci get určenou pro přečtení jednoho prvku ze seznamu na zadaném indexu (indexuje se od nuly) a taktéž o funkce nazvané příznačně first a last pro přístup k prvnímu resp. k poslednímu prvku. Povšimněte si, že funkce get akceptuje i záporné indexy pro přístup k prvkům od konce vektoru, což vlastně není překvapivé, protože se tato funkce překládá na Pythonovské vektor[index]:

=> ; výraz pro přečtení prvku vektoru
=> (get vektor 1)
2
=> (get vektor -1)
4
=> (get vektor -2)
3
 
=> ; speciální funkce pro významné prvky vektoru
=> (first vektor)
1
=> (last vektor)
4

Další funkce se jmenuje rest a její chování již více odpovídá Clojure, protože tato funkce nevrací seznam ani vektor, ale iterátor, což je možné považovat za obdobu líné sekvence. Pro převod iterátoru na vektor se používá funkce list, což je sice matoucí, ale musíme si uvědomit, že snahou je zachování co největší úrovně kompatibility s Pythonem:

=> ; převod na sekvenci bez prvního prvku
=> (rest vektor)
<itertools.islice object at 0x7f1237a16f98>
 
=> ; zpětný převod sekvence na vektor
=> (list (rest vektor))
[2, 3, 4]
 
=> ; vylepšený způsob zápisu předchozího výrazu
=> (-> vektor rest list)
[2, 3, 4]

Následuje ukázka použití vektoru vektorů neboli matic, ať již pravidelných nebo nepravidelných:

=> ; dvourozměrný vektor (matice)
=> (setv matice [[1 2 3] [4 5 6] [7 8 9]])
 
=> matice
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
 
=> ; nepravidelná matice
=> (setv matice2 [[1] [2 3] [4 5 6] [7 8 9 10]])
 
=> matice2
[[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]

Při snaze o vytvoření „plochého“ vektoru použijeme funkci flatten:

=> (flatten matice)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

4. Modifikace obsahu vektoru s využitím funkcí cut, del, assoc a metody append

Další užitečná funkce cut slouží pro vytvoření dalšího vektoru z vybraných prvků vektoru prvního. Překlad této funkce odpovídá Pythonovskému vektor[od:do], a to včetně možnosti použití záporných indexů:

=> ; získání subvektoru
=> (cut vektor 1 5)
[2, 3, 4]
=> (cut vektor 1)
[2, 3, 4]
=> (cut vektor -5 -2)
[1, 2]
=> (cut vektor -3 -2)
[2]

Třetím nepovinným parametrem je možné určit krok, ať již kladný či záporný:

=> (setv vektor2 (list (range 20)))
 
=> ; sudé prvky
=> (cut vektor2 2 -1 2)
[2, 4, 6, 8, 10, 12, 14, 16, 18]
= 
=> ; otočení vektoru
=> (cut vektor2 -1 0 -1)
[19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
= 
=> ; otočení se získáním jen lichých prvků
=> (cut vektor2 -1 0 -2)
[19, 17, 15, 13, 11, 9, 7, 5, 3, 1]

Pro změnu hodnoty prvku ve vektoru se používá makro assoc, ovšem musíme si dát pozor na to, aby prvek s daným indexem již ve vektoru existoval:

=> ; změna prvku ve vektoru je možná
=> (assoc vektor 2 42)
= 
=> ; POZOR: vyhodí výjimku!
=> (assoc vektor 10 -1)
Traceback (most recent call last):
  File "/home/tester/.local/lib/python3.6/site-packages/hy/importer.py", line 198, in hy_eval
    eval(ast_compile(_ast, <eval_body>", "exec"), namespace)
  File "<eval_body>", line 1, in <module>
IndexError: list assignment index out of range
Poznámka: povšimněte si, že makro asocc nevrací žádnou hodnotu, resp. přesněji řečeno vrací None (odpovídá nil v LISPu). Je tomu tak z toho důvodu, že dochází k modifikaci původního vektoru.

Při mazání prvků z vektoru použijeme funkci del, typicky společně s funkcí cut:

=> (setv vektor2 ["A" "B" "C" "D" "E" "F"])
 
=> vektor2
['A', 'B', 'C', 'D', 'E', 'F']
 
=> (cut vektor2 2 4)
['C', 'D']
 
=> (del (cut vektor2 2 4))
 
=> vektor2
 
['A', 'B', 'E', 'F']
 
=> (-> (cut vektor2 2 4) del)
 
=> vektor2
['A', 'B']

A konečně pro přidání nového prvku do vektoru můžete použít metodu .append, která se zapisuje dvěma způsoby – funkcionálně nebo objektově:

=> ; přidání prvku do vektoru (na jeho konec)
=> (.append vektor 5)
 
=> ; přidání prvku do vektoru (na jeho konec)
=> (vektor.append 5)

5. Funkce a makra určená pro práci se slovníky

Další funkce a makra, která si popíšeme, se týkají práce se slovníky (dictionary). I v této oblasti vidíme inspiraci programovacím jazykem Clojure (konstruktory slovníků), ovšem současně je patrná poměrně úzká návaznost i na samotný jazyk Python. Nejprve si zopakujme, jak vypadá konstruktor slovníku. Ten je jednoduchý – všechny dvojice klíč+hodnota se uvedou do složených závorek:

=> {"prvni" "first" "druhy" "second" "treti" "third"}
{'prvni': 'first', 'druhy': 'second', 'treti': 'third'}

Pro získání hodnoty uložené pod nějakým klíčem se opět používá funkce get, které se ovšem pochopitelně namísto indexu prvku předává klíč. V případě, že prvek s daným klíčem není nalezen, dojde k běhové výjimce:

=> (setv d1 {:id 1 :name "Eda" :surname "Wasserfall"})
 
=> (get d1 :name)
'Eda'
=> (get d1 :xyname)
Traceback (most recent call last):
  File "/home/tester/.local/lib/python3.6/site-packages/hy/importer.py", line 201, in hy_eval
    return eval(ast_compile(expr, "<eval>", "eval"), namespace)
  File "<eval>", line 1, in <module>
KeyError: '\ufdd0:xyname'

V případě, že se ve funkci get použije větší množství selektorů (indexů popř. klíčů), je možné vybírat hodnoty z vnořených datových struktur. Opět si to ukažme na jednoduchém příkladu, konkrétně na slovníku, který v jednom prvku obsahuje seznam:

=> (setv d2 {:id 1 :name "Eda" :surname "Wasserfall" :actors ["Genadij Rumlena" "Pavel Vondruška"]})
 
=> (get d2 :actors 1)
'Pavel Vondruška'
 
=> (get d2 :actors 0)
'Genadij Rumlena'

I u slovníků lze použít funkci assoc pro přidání další dvojice klíč+hodnota. Slovník je tedy možné vytvořit postupně:

=> (setv d3 {})
 
=> (assoc d3 :id 10)
 
=> (assoc d3 :name "Eda")
 
=> (assoc d3 :surname "Wasserfall")
 
Poznámka: funkce assoc mění původní slovník, na rozdíl od stejně pojmenované funkce v jazyku Clojure, která vytváří slovník nový.

Funkce assoc dokáže přepsat hodnotu prvku, a to ve chvíli, kdy použijeme stejný klíč, který je již ve slovníku obsažen:

=> (assoc d3 :id 10)

Vymazání dvojice klíč+hodnota zajišťuje funkce del:

=> (del (get d3 :surname))
Poznámka: v jazyce Clojure pro tento účel slouží funkce dissoc.

6. Generátorová notace slovníku a použití funkce zip pro vytvoření slovníku

Velmi užitečná je funkce nazvaná dict-comp. Tato funkce implementuje známou „generátorovou notaci slovníku“. Zkusme si nyní vytvořit slovník s deseti prvky, přičemž každý prvek (dvojice klíč+hodnota) obsahuje číslo+řetězec obsahující stejné cifry:

=> (dict-comp x (str x) [x (range 1 11)])
{1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10'}
 
=> (dict-comp x (* x x) [x (range 1 11)])
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
Poznámka: další srovnání s Clojure. V Clojure se pro generátorovou notaci seznamu popř. slovníku používá funkce for, která je ovšem v Hy rezervována pro implementaci programové smyčky stejného jména.

Při tvorbě slovníku je dokonce možné použít i filtraci prvků:

=> (dict-comp x (str x) [x (range 1 11)] (even? x))
{2: '2', 4: '4', 6: '6', 8: '8', 10: '10'}
 
=> (dict-comp x (str x) [x (range 1 11)] (odd? x))
{1: '1', 3: '3', 5: '5', 7: '7', 9: '9'}

Samozřejmě lze využít i vlastní funkci (predikát) pro filtraci:

=> (defn div3 [n] (= 0 (% n 3)))
 
=> (dict-comp x (* x x) [x (range 1 11)] (div3 x))
{3: 9, 6: 36, 9: 81}

Další možností, jak vytvořit slovník, je použití funkce zip, ovšem výsledek je nutné na slovník převést:

=> (dict (zip [:id :name :surname] [1 "Eda" "Wasserfall"]))

Nepatrně složitější příklady:

=> (repeat "A" 10)
repeat('A', 10)
 
=> (dict (zip (range 10) (repeat "A" 10)))
{0: 'A', 1: 'A', 2: 'A', 3: 'A', 4: 'A', 5: 'A', 6: 'A', 7: 'A', 8: 'A', 9: 'A'}
 
=> (-> (zip (range 10) (range 10 1 -1)) dict)
{0: 10, 1: 9, 2: 8, 3: 7, 4: 6, 5: 5, 6: 4, 7: 3, 8: 2}

Počet hodnot může přesahovat počet klíčů, ovšem hodnoty, které nelze na klíče namapovat, se budou jednoduše ignorovat:

=> (-> (zip (range 10) (range 50 1 -1)) dict)
{0: 50, 1: 49, 2: 48, 3: 47, 4: 46, 5: 45, 6: 44, 7: 43, 8: 42, 9: 41}

7. Pravidla pro pojmenování funkcí v jazyku Hy

V programovacím jazyku Hy je možné funkce pojmenovávat stejným způsobem, jako je tomu u většiny dalších dialektů LISPu. Podívejme se tedy na několik typických příkladů. Funkce, jejichž jména jsou složena z většího množství slov, se většinou zapisují s využitím pomlčky:

; příklad jména funkce zapisované tak, jak je zvykem v LISPu
(defn calculate-multiplication
    [x y]
    (* x y))

Predikáty, tj. funkce, které na základě hodnoty svého parametru (parametrů) vracejí pravdivostní hodnotu True/False, se typicky zapisují s otazníkem na konci:

; predikáty
(defn zero?
    [x]
    (== x 0))
 
(defn even?
    [x]
    (zero? (mod x 2)))
 
(defn odd?
    [x]
    (not (even? x)))

Konverzní funkce mohou ve svém jménu používat šipku ->, ovšem takové funkce nebude možné volat z Pythonu:

; konverzní funkce
(defn string->bool
    [s]
    (== s "true"))
 
(defn deg->rad
    [angle]
    (* angle (/ 3.1415 180)))

A konečně privátní popř. skryté funkce se mohou zapisovat se dvěma hvězdičkami nebo, což je lepší, s pomlčkou na začátku:

; privátní funkce
(defn **hidden**
    [x]
    (+ x 1))
 
; privátní funkce - lepší pojmenování
(defn -hidden
    [x]
    (+ x 1))

Podívejme se nyní na to, jak se takto pojmenované funkce přeloží do Pythonu. Povšimněte si, že ne všechny názvy jsou korektní Pythonovské identifikátory. Také stojí za zapamatování způsob překladu názvů predikátů:

from hy.core.language import is_even, is_odd, is_zero
 
 
def calculate_multiplication(x, y):
    return x * y
 
 
def is_zero(x):
    return ==(x, 0)
 
 
def is_even(x):
    return is_zero(mod(x, 2))
 
 
def is_odd(x):
    return not is_even(x)
 
 
def string_>bool(s):
    return ==(s, 'true')
 
 
def deg_>rad(angle):
    return angle * (3.1415 / 180)
 
 
def *HIDDEN*(x):
    return x + 1
 
 
def _hidden(x):
    return x + 1

8. Volání funkcí a metod definovaných v Pythonu

V této kapitole si ukážeme, jakým způsobem můžeme v jazyku Hy použít funkce a metody definované v Pythonu. Pro tento účel si připravíme malý testovací modul nazvaný test_module.py, který obsahuje jak několik funkcí, tak i třídu s konstruktorem a jednou metodou. Navíc je deklarována jedna konstanta:

THE_ANSWER = 42
 
 
def multiply_two_numbers(x, y):
    return x * y
 
 
class uber_class:
    def __init__(self, x):
        self._x = x
 
    def compute_square(self):
        return self._x * self._x

Použití tříd a metod z modulu datetime je snadné. Povšimněte si, že metody lze volat jak stylem (.metoda objekt parametry), tak i stylem (objekt.metoda parametry):

(import [datetime [date :as d]])
 
(setv date (d 2018 02 28))
(print date)
(print date.year)
(print date.month)
(print date.day)
 
(setv now1 (.today d))
(print now1)
(setv now2 (d.today))
(print now2)

Příklad použití konstanty a funkce z našeho testovacího modulu ukazuje, že lze použít jak původní jména (vytvořená podle konvencí Pythonu), tak i jména odpovídající konvencím programovacího jazyka Hy:

(import [test_module [*]])
 
(print THE_ANSWER)
(print *the-answer*)
(print (multiply_two_numbers 6 7))
(print (multiply-two-numbers 6 7))

Alternativní způsob importu do vlastního jmenného prostoru t:

(import [test_module :as t])
 
(print t.THE_ANSWER)
(print t.*the-answer*)
(print (t.multiply_two_numbers 6 7))
(print (t.multiply-two-numbers 6 7))

Konstrukce objektu a volání jeho metody (opět oběma podporovanými způsoby):

(setv u (uber_class 42))
(print u)
(print (u.compute_square))
(print (.compute_square u))

9. Volání funkcí definovaných v jazyku Hy z Pythonu

Nyní si ukážeme opačný směr spolupráce mezi programovacím jazykem Hy a Pythonem. Nejdříve nadefinujeme několik funkcí v jazyku Hy a posléze tyto funkce (resp. jen ty funkce, které mají korektní jméno) zavoláme z Pythonu. Nutné přitom je, aby se použilo následující pojmenování souboru s modulem naprogramovaným v Hy:

jméno_modulu.hy

Vytvoříme tedy soubor nazvaný interop2.hy, který bude mít následující obsah:

; Běžná funkce zapisovaná ve stylu LISPu
(defn calculate-multiplication
    [x y]
    (* x y))
 
 
; Predikáty
(defn zero?
    [x]
    (= x 0))
 
(defn even?
    [x]
    (zero? (% x 2)))
 
(defn odd?
    [x]
    (not (even? x)))
 
 
; Konverzní funkce
(defn string->bool
    [s]
    (= s "true"))
 
(defn deg->rad
    [angle]
    (* angle (/ 3.1415 180)))
 
 
; Privátní funkce
(defn -hidden
    [x]
    (+ x 1))
 
; Funkce psaná velkými písmeny
(defn *major*
    [x]
    (+ x 1))

Všechny funkce, u nichž se podařilo vytvoření korektního pythonovského jména, lze zavolat přímo z Pythonu, a to stejným způsobem, jako jakékoli jiné funkce. Nesmíme zapomenout na import modulu hy a samozřejmě i testovaného modulu (ten se nijak nepřekládá!):

import hy
from interop2 import *
 
print(calculate_multiplication(6, 7))
 
print(is_zero(0))
print(is_zero(1))
print(is_zero(2))
 
print(is_even(0))
print(is_even(1))
print(is_even(2))
 
print(is_odd(0))
print(is_odd(1))
print(is_odd(2))
 
print(MAJOR(1))
 
import interop2
print(interop2._hidden(1))
Můžeme vidět, že interoperabilita Hy → Python i Python → Hy je na velmi dobré úrovni a oba programátor může vcelku bez problémů využívat možností obou jazyků v jediném projektu.

10. Použití maker v jazyku Hy

V navazujících kapitolách se seznámíme s možnostmi makrosystému nabízeného programovacím jazykem Hy. Před zápisem uživatelských maker si však ukažme takzvaná reader makra, která jsou aplikována již ve chvíli načítání jednotlivých výrazů do interpretru. Samotná reader makra jsou velmi jednoduchá, protože nemají přístup k AST (jsou aplikována příliš brzy):

# Makro Název Význam
1 ; comment umožňuje obejít zápis (comment nějaký text) u komentářů
2 ' quote nahrazuje zápis (quote …)
3 ` syntax-quote provádí plnou kvalifikaci symbolů + zde lze použít makra ~ a ~@
4 ~ unquote zajistí, že se vyhodnotí pouze označená část formy (= provede substituci této části výsledkem)
5 ~@ unquote-splicing podobné předchozími makru, ovšem výsledná sekvence se vloží ve formě samostatných prvků do „obalující“ sekvence
6 # dispatch má různé funkce: donutí reader, aby použil makro z jiné tabulky maker

Makro dispatch (poslední v předchozí tabulce) má ve skutečnosti několik významů v závislosti na tom, jaký znak je uveden ihned po křížku (#):

# Dvojice znaků Způsob použití Význam
1 #{ #{prvky} zápis množiny
2 #_ #_text text je ignorován – alternativní způsob komentáře

V uživatelských makrech (těch plnohodnotných) se velmi často používají reader makra syntax-quote a unquote.

11. Smyčka REPL (Read-Eval-Print-Loop) a systém maker v tradičních LISPech

Jednoduchý interpret LISPu mohl být teoreticky implementován pouze s využitím trojice funkcí read (načtení výrazu/formy ze standardního vstupu), print (tisk výsledku vyhodnocení výrazu/formy na standardní výstup), eval (většinou rekurzivně implementovaná funkce určená pro vyhodnocení načtené formy), které byly doplněny speciální formou či makrem loop (nekonečná smyčka – při striktním pohledu se v tomto případě nemůže jednat o funkci). Ve skutečnosti je však samozřejmě nutné, aby byl prakticky použitelný programovací jazyk doplněn o alespoň minimální množství základních funkcí a speciálních forem. V případě původního LISPu se jednalo o sedm funkcí a dvě speciální formy: atom, car, cdr, cond, cons, eq, quote, lambda a konečně label.

Původně relativně velmi jednoduše a přitom elegantně implementovaný interpret programovacího jazyka LISP se postupně začal vyvíjet a jednou z nových a přitom mocných technik, které do něj byly přidány, jsou takzvaná makra, která se však v mnoha ohledech liší od maker používaných například v programovacích jazycích C a C++. Zatímco v céčku jsou makra zpracovávána poměrně „hloupým“ preprocesorem, který dokáže provádět textové substituce, načítat vkládané soubory a vyhodnocovat jednoduché podmínky, mohou makra implementovaná v programovacím jazyce LISP pracovat přímo se zadávanými formami, které makra mohou různým způsobem modifikovat – přitom se zde využívá faktu, že v LISPu a tudíž i v jazyku Hy jsou programy reprezentovány ve formě (obvykle rekurzivně vnořených) seznamů, a změnou obsahu těchto seznamů lze vlastně přímo manipulovat s takzvaným abstraktním syntaktickým stromem (AST – Abstract Syntax Tree).

Není bez zajímavosti, že s AST se v LISPu nebo Hy 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ý). Jinými slovy to znamená, že se při tvorbě maker musíme seznámit pouze se způsobem zápisu maker, ale v samotných makrech se mohou používat funkce, které jsme si již v tomto článku popsali – většinou se bude jednat o funkce pro práci se seznamy, což je vzhledem ke způsobu reprezentace programů (jako do sebe vnořených seznamů) pochopitelné.

Poznámka: tato vlastnost se nazývá homoikonicita a v důsledku znamená, že dialekty LISPu vlastně stojí na vrcholu hierarchie programovacích jazyků. Jakoukoli novou sémantickou konstrukci, která se objeví v jiném programovacím jazyku, je totiž možné díky homoikonicitě implementovat i v LISPech (za předpokladu že LISP je Turingovsky úplný, což bezpochyby je).

12. Využití eval

V předchozí kapitole jsme si řekli, že do funkce eval je možné předat pouze korektně zapsanou formu. V některých případech je však určitý výraz nebo i větší část programů dostupná pouze ve formě řetězce – ten může být přečten například ze souboru, zadán uživatelem v nějakém GUI dialogu atd. Problém nastane v případě, kdy se pokusíme tento řetězec předat funkci eval v domnění, že se předávaný řetězec „automagicky“ bude transformovat na korektní formu a ta se následně vyhodnotí.

Předpoklad, že eval bude jako svůj parametr akceptovat řetězec, může vycházet ze zkušeností vývojáře s jinými programovacími jazyky, kde tomu tak skutečně může být, ovšem v Hy a ani v dalších dialektech LISPu to neplatí a pro toto chování jsou i dobré důvody – mimo jiné i bezpečnost (a taktéž to, že parsování řetězce skutečně není prací pro eval). Podívejme se nyní, co se stane, pokud se pokusíme nechat vyhodnotit řetězec obsahující zápis korektní formy, ovšem pouze v textové podobě:

; vytvoření nové globální proměnné
; a přiřazení SEZNAMU do této proměnné
=> (setv hello-code '(print "Hello world!"))
 
; hodnotu proměnné (tedy obsah seznamu)
; lze samozřejmě kdykoli získat
=> hello-code
HyExpression([
  HySymbol('print'),
  HyString('Hello world!')])
 
; i když proměnná obsahuje seznam s korektním
; voláním funkce, není možné použít následující
; formu pro zavolání této funkce
=> (hello-code)
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>
TypeError: 'HyExpression' object is not callable
 
; namísto toho se musí použít funkce eval
=> (eval hello-code)
Hello world!

13. Makra „quote“ a „syntax-quote“

Konečně se dostáváme k zajímavým a užitečným reader makrům. 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:

=> '42
HyInteger(42)
=> '(1 2 3)
HyExpression([
  HyInteger(1),
  HyInteger(2),
  HyInteger(3)])
=> '[1 2 3]
HyList([
  HyInteger(1),
  HyInteger(2),
  HyInteger(3)])
=> [1 2 3]
[1, 2, 3]
=> '(* 6 7)
HyExpression([
  HySymbol('*'),
  HyInteger(6),
  HyInteger(7)])
=> (* 6 7)
42

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:

=> `42
HyInteger(42)
=> `(1 2 3)
HyExpression([
  HyInteger(1),
  HyInteger(2),
  HyInteger(3)])
=> `[1 2 3]
HyList([
  HyInteger(1),
  HyInteger(2),
  HyInteger(3)])

Další možnosti:

=> `(* 6 7)
HyExpression([
  HySymbol('*'),
  HyInteger(6),
  HyInteger(7)])
=> `(str "Hello" "world")
HyExpression([
  HySymbol('str'),
  HyString('Hello'),
  HyString('world')])
=> `[* seq str xyzzy neznamy]
HyList([
  HySymbol('*'),
  HySymbol('seq'),
  HySymbol('str'),
  HySymbol('xyzzy'),
  HySymbol('neznamy')])

Makro nazvané unquote, které se zapisuje pomocí 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
=> '(1 2 (* 6 7) (/ 4 2))
HyExpression([
  HyInteger(1),
  HyInteger(2),
  HyExpression([
    HySymbol('*'),
    HyInteger(6),
    HyInteger(7)]),
  HyExpression([
    HySymbol('/'),
    HyInteger(4),
    HyInteger(2)])])
 
; makro syntax-quote zakáže vyhodnocení celého seznamu
=> `(1 2 (* 6 7) (/ 4 2))
HyExpression([
  HyInteger(1),
  HyInteger(2),
  HyExpression([
    HySymbol('*'),
    HyInteger(6),
    HyInteger(7)]),
  HyExpression([
    HySymbol('/'),
    HyInteger(4),
    HyInteger(2)])])

Použití ~ uvnitř `:

; pomocí ~ vynutíme vyhodnocení podvýrazu (* 6 7)
=> `(1 2 ~(* 6 7) (/ 4 2))
HyExpression([
  HyInteger(1),
  HyInteger(2),
  42,
  HyExpression([
    HySymbol('/'),
    HyInteger(4),
    HyInteger(2)])])
 
; pomocí ~ vynutíme vyhodnocení podvýrazu (/ 4 2)
=> `(1 2 (* 6 7) ~(/ 4 2))
HyExpression([
  HyInteger(1),
  HyInteger(2),
  HyExpression([
    HySymbol('*'),
    HyInteger(6),
    HyInteger(7)]),
  2.0])
 
; pomocí dvou ~ vynutíme vyhodnocení obou podvýrazů
=> `(1 2 ~(* 6 7) ~(/ 4 2))
HyExpression([
  HyInteger(1),
  HyInteger(2),
  42,
  2.0])

Podobným způsobem pracuje i makro ~@, 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
=> (setv s '(1 2 3))
 
=> '(1 2 3 (cons s s))
HyExpression([
  HyInteger(1),
  HyInteger(2),
  HyInteger(3),
  HyExpression([
    HySymbol('cons'),
    HySymbol('s'),
    HySymbol('s')])])
=> `(1 2 3 (cons s s))
HyExpression([
  HyInteger(1),
  HyInteger(2),
  HyInteger(3),
  HyExpression([
    HySymbol('cons'),
    HySymbol('s'),
    HySymbol('s')])])
=> `(1 2 3 ~(cons s s))
HyExpression([
  HyInteger(1),
  HyInteger(2),
  HyInteger(3),
  HyExpression([
    HyExpression([
      HyInteger(1),
      HyInteger(2),
      HyInteger(3)]),
    HyInteger(1),
    HyInteger(2),
    HyInteger(3)])])
=> `(1 2 3 ~@(cons s s))
HyExpression([
  HyInteger(1),
  HyInteger(2),
  HyInteger(3),
  HyExpression([
    HyInteger(1),
    HyInteger(2),
    HyInteger(3)]),
  HyInteger(1),
  HyInteger(2),
  HyInteger(3)])

14. Praktické použití – jednoduchá makra

Uživatelská makra se vytváří s využitím defmacro. Podívejme se nyní na velmi jednoduché makro, které po svém zavolání (v době zpracování vstupního textu!) provede expanzi na (print 'výraz), tj. vypíše se původní (nevyhodnocený!) výraz, což se může hodit například při ladění programů:

(defmacro print-expression-1
    [expression]
    `(print '~expression))

Makro můžeme upravit i tak, aby se nejprve vypsal nevyhodnocený výraz a posléze i jeho výsledek. Použijeme zde speciální formu do pro spojení většího množství funkcí do jediného bloku. Povšimněte si, že celý blok je uvozen zpětným apostrofem a uvnitř bloku tedy můžeme využít ~:

(defmacro print-expression-2
    [expression]
    `(do (print '~expression)
         (print ~expression)))

Příklady použití:

(print-expression-1 (* 6 7))
 
HyExpression([
  HySymbol('*'),
  HyInteger(6),
  HyInteger(7)])
 
 
(print-expression-2 (* 6 7))
 
HyExpression([
  HySymbol('*'),
  HyInteger(6),
  HyInteger(7)])
42

Další makro nalezneme v doplňkové knihovně jazyka Hy. Umožňuje pro každý prvek seznamu zavolat nějakou funkci, která může mít vedlejší efekt. Uvnitř této funkce je příslušný prvek seznamu představován symbolem it. Makro nazvané ap-each vypadá následovně a jeho chování při expanzi je zřejmý, protože známe význam ` ~ i ~@:

(defmacro ap-each [lst &rest body]
  `(for [it ~lst] ~@body))

Příklad použití:

=> (ap-each [1 2 3] (print it))
1
2
3
 
=> (ap-each [1 2 3] (print (* it it)))
1
4
9

15. Transpřeklad programů z jazyka Hy do Pythonu

S využitím nástrojů nazvaných hy2py a hy2py3 lze provést překlad zdrojových kódů naprogramovaných v jazyce Hy do Pythonu 2 či do Pythonu 3. Možnosti těchto nástrojů si ukážeme na třech variantách výpočtu faktoriálu. Povšimněte si, že výsledkem jsou většinou velmi pěkně čitelné zdrojové kódy, což při transpřekladu není vždy zvykem:

; 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))

Výsledek přeložený nástrojem hy2py3 – příkaz od příkazu obdoba předchozího skriptu:

from hy.core.language import inc, is_neg, reduce
from hy.core.shadow import *
 
 
def factorial(n):
    if is_neg(n):
        raise ValueError('natural number expected')
        _hy_anon_var_1 = None
    else:
        _hy_anon_var_1 = reduce(*, range(1, inc(n)))
    return _hy_anon_var_1
 
 
print(factorial(10))
for n in range(1, 11):
    print(n, factorial(n))
print(factorial(-10))

Druhá varianta, tentokrát rekurzivního výpočtu:

; rekurzivní výpočet faktoriálu
 
(defn factorial
    [n]
    (if (<= n 1)
        1
        (* n (factorial (- n 1)))))
 
(print (factorial 10))
 
(for [n (range 1 11)]
     (print n (factorial n)))

Výsledek přeložený nástrojem hy2py3; opět prakticky totožný s originálem:

def factorial(n):
    return 1 if n <= 1 else n * factorial(n - 1)
 
 
print(factorial(10))
for n in range(1, 11):
    print(n, factorial(n))

Třetí varianta, tentokrát založená na TCO (tail call optimization):

; 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)))

Výsledek přeložený nástrojem hy2py3. Nyní si transpřekladač musel vypomoci takzvanou „trampolínou“ (trampoline):

from hy.core.language import dec, is_zero
 
 
def factorial(n):
    from hy.contrib.loop import __trampoline__
 
    @__trampoline__
    def _;recur_fn|1236(cnt, acc):
        return acc if is_zero(cnt) else _;recur_fn|1236(dec(cnt), acc * cnt)
    _;recur_fn|1235 = _;recur_fn|1236
    return _;recur_fn|1235(n, 1)
 
 
print(factorial(10))
for n in range(1, 11):
    print(n, factorial(n))

16. Složitější příklad – vygenerování SVG souboru s logem

Jako příklad použití některých základních funkcí z modulů hy.contrib.loop a math si ukažme krátký prográmek, který vygeneruje obrázek ukázaný níže. Při výpočtu vektorového obrázku ukládaného do formátu SVG se používají funkce sinus a kosinus, při ukládání obrázku pak uživatelsky definovaná funkce spit založená na pythonovských I/O funkcích:

; vykreslení vektorového loga do souboru (formát SVG)
 
(require [hy.contrib.loop [loop]])
(import [math [sin cos]])
 
(setv s 480)
 
(defn spit
    [filename content]
    (with [fout (open filename "w")]
        (.write fout content)))
 
(->>
    (+ "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='" (str s) "' height='" (str s) "'>"
         (loop [[i 0] [R 255] [G 255] [B 0] [out ""]]
             (setv r (- 128 i)
                   a (/ i 12.)
                   b (+ i 80)
                   x (+ (/ s 2) (* b (cos a)))
                   y (+ (/ s 2) (* b (sin a)))
                   p (+ "<circle cx='" (str x) "' cy='" (str y) "' r='" (str r) "' ")
                   q (+ "fill='rgb(" (str R) "," (str G) "," (str B) ")' style='fill-opacity:.06'/>\n"))
             (if (< i 128)
                 (recur (inc i) (- R 2) G (+ B 2) (+ out p q p "fill='none' stroke='black'/>\n"))
                 out))
         "</svg>")
    (spit "logo.svg"))
Logo.clj

Obrázek 1: Vektorový obrazec vytvořený předchozím demonstračním příkladem.

17. Porovnání SVG generátoru s podobným příkladem naprogramovaným v Clojure a Pixie

Pro porovnání si předchozí skript ukažme ve variantě naprogramované v Clojure:

(def s 480)
 
(->>
    (str (format "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='%d' height='%d'>" s s)
         (loop [i 0 R 255 G 255 B 0 o ""]
             (let [r (- 128 i)
                   a (/ i 12.)
                   b (+ i 80)
                   x (+ (/ s 2) (* b (Math/cos a)))
                   y (+ (/ s 2) (* b (Math/sin a)))
                   p (format"<circle cx='%f' cy='%f' r='%d' " x y r)
                   q (str "fill='rgb(" R "," G "," B ")' style='fill-opacity:.06'/>\n")]
                   (if (< i 128)
                       (recur (inc i) (- R 2) G (+ B 2) (str o p q p "fill='none' stroke='black'/>\n"))
                       o)))
         "</svg>")
    (spit "logo.svg"))

A taktéž ve variantě vytvořené v programovacím jazyku Pixie:

(ns logo (:require [pixie.math :refer :all]
                   [pixie.io :refer :all]))
 
(def s 480)
 
(->>
    (str "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='" s "' height='" s "'>"
    (loop [i 0 R 255 G 255 B 0 o ""]
        (let [r (- 128 i)
              a (/ i 12.)
              b (+ i 80)
              x (+ (/ s 2) (* b (cos a)))
              y (+ (/ s 2) (* b (sin a)))
              c (str R "," G "," B)
              p (str "<circle cx='" x "' cy='" y "' r='" r "' ")
              q (str "fill='rgb(" R "," G "," B ")' style='fill-opacity:.06'/>\n")]
              (if (< i 128)
                  (recur (inc i) (- R 2) G (+ B 2) (str o p q p "fill='none' stroke='black'/>\n"))
                  o)))
         "</svg>")
    (spit "logo.svg"))

Vidíme, že až na několik maličkostí jsou všechny tři programy prakticky shodné.

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:

Kromě zdrojových kódů příkladů najdete v repositáři i soubory pro Python 2 i Python 3 vygenerované výše zmíněnými nástroji hy2py a hy2py3.

19. Odkazy na předchozí části seriálu o jazyku Clojure

  1. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  2. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  3. 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/
  4. 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/
  5. 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/
  6. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  7. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  8. 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/
  9. 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/
  10. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  11. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  12. 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/
  13. 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/
  14. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  15. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  16. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  17. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  18. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  19. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  20. 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/
  21. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
  22. Leiningen: nástroj pro správu projektů napsaných v Clojure
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/
  23. 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/
  24. 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/
  25. 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/
  26. 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/
  27. 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/
  28. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  29. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  30. 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/
  31. 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/
  32. 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/
  33. 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/
  34. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  35. 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/
  36. 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/
  37. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  38. 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/
  39. Programovací jazyk Clojure a práce s Gitem (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/
  40. Programovací jazyk Clojure – triky při práci s řetězci
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/
  41. Programovací jazyk Clojure – triky při práci s kolekcemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/
  42. Programovací jazyk Clojure – práce s mapami a množinami
    http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/
  43. Programovací jazyk Clojure – základy zpracování XML
    http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/
  44. Programovací jazyk Clojure – testování s využitím knihovny Expectations
    http://www.root.cz/clanky/programovaci-jazyk-clojure-testovani-s-vyuzitim-knihovny-expectations/
  45. 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/
  46. Enlive – výkonný šablonovací systém pro jazyk Clojure
    http://www.root.cz/clanky/enlive-vykonny-sablonovaci-system-pro-jazyk-clojure/
  47. 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/
  48. Novinky v Clojure verze 1.8.0
    http://www.root.cz/clanky/novinky-v-clojure-verze-1–8–0/
  49. 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/
  50. 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/
  51. 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/
  52. Vytváříme IRC bota v programovacím jazyce Clojure
    http://www.root.cz/clanky/vytvarime-irc-bota-v-programovacim-jazyce-clojure/
  53. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  54. Multimetody v Clojure aneb polymorfismus bez použití OOP
    https://www.root.cz/clanky/multimetody-v-clojure-aneb-polymorfismus-bez-pouziti-oop/
  55. 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/
  56. Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
    https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/
  57. Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
    https://www.root.cz/clanky/pro­gramovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/
  58. Novinky v Clojure verze 1.9.0
    https://www.root.cz/clanky/novinky-v-clojure-verze-1–9–0/
  59. 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/
  60. 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/
  61. 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/
  62. 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/

20. Odkazy na Internetu

  1. Python becomes a platform
    https://khinsen.wordpress­.com/2012/03/15/python-becomes-a-platform/
  2. Python becomes a platform. Thoughts on the release of clojure-py
    https://news.ycombinator.com/i­tem?id=3708974
  3. SchemePy
    https://pypi.org/project/SchemePy/
  4. lispy
    https://pypi.org/project/lispy/
  5. Lython
    https://pypi.org/project/Lython/
  6. Lizpop
    https://pypi.org/project/lizpop/
  7. Budoucnost programovacích jazyků
    http://www.knesl.com/budoucnost-programovacich-jazyku
  8. LISP Prolog and Evolution
    http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html
  9. List of Lisp-family programming languages
    https://en.wikipedia.org/wi­ki/List_of_Lisp-family_programming_languages
  10. clojure_py na indexu PyPi
    https://pypi.python.org/py­pi/clojure_py
  11. PyClojure
    https://github.com/eigenhom­bre/PyClojure
  12. Hy na GitHubu
    https://github.com/hylang/hy
  13. Hy: The survival guide
    https://notes.pault.ag/hy-survival-guide/
  14. Hy běžící na monitoru terminálu společnosti Symbolics
    http://try-hy.appspot.com/
  15. Welcome to Hy’s documentation!
    http://docs.hylang.org/en/stable/
  16. Hy na PyPi
    https://pypi.org/project/hy/#des­cription
  17. Getting Hy on Python
    https://lwn.net/Articles/596626/
  18. Programming Can Be Fun with Hy
    https://opensourceforu.com/2014/02/pro­gramming-can-fun-hy/
  19. Přednáška o projektu Hy (pětiminutový lighttalk)
    http://blog.pault.ag/day/2013/04/02
  20. Hy (Wikipedia)
    https://en.wikipedia.org/wiki/Hy
  21. Clojure home page
    http://clojure.org/
  22. Clojure Sequences
    http://clojure.org/sequences
  23. Clojure Data Structures
    http://clojure.org/data_structures
  24. Clojure
    https://en.wikipedia.org/wiki/Clojure
  25. Clojars:
    https://clojars.org/
  26. Seznam knihoven na Clojars:
    https://clojars.org/projects
  27. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  28. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  29. 4Clojure
    http://www.4clojure.com/
  30. ClojureDoc (rozcestník s dokumentací jazyka Clojure)
    http://clojuredocs.org/
  31. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  32. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  33. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  34. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  35. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  36. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  37. Threading macro (dokumentace k jazyku Clojure)
    https://clojure.github.io/clo­jure/clojure.core-api.html#clojure.core/->
  38. Understanding the Clojure → macro
    http://blog.fogus.me/2009/09/04/un­derstanding-the-clojure-macro/
  39. Emacs LISP
    https://www.root.cz/clanky/historie-vyvoje-textovych-editoru-eine-zwei-emacs/#k08
  40. Programovací jazyk LISP a LISP machines
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lisp-a-lisp-machines/
  41. 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/
  42. Programovací jazyky používané (nejen) v SSSR (část 3 – LISP)
    https://www.root.cz/clanky/pro­gramovaci-jazyky-pouzivane-nejen-v-nbsp-sssr-cast-3-ndash-lisp/
Našli jste v článku chybu?