Obsah
1. Multimetody v Clojure aneb polymorfismus bez použití OOP
2. Problém typický pro třídní OOP: je kružnice speciálním případem elipsy?
4. První varianta funkce heading
6. První vylepšení – rozlišení typu parametru předaného funkci heading
7. Definice multimetody a k ní příslušné výběrové (dispatch) funkce
8. Volání multimetody na základě zjištěného typu parametru
10. Hierarchie typů a způsob řešení „kolizí“
11. Programátorem zvolená preference metod
12. Finální varianta multimetody heading
13. Odkazy na předchozí části tohoto seriálu
1. Multimetody v Clojure aneb polymorfismus bez použití OOP
V diskusi, která se vedla (resp. přesněji řečeno stále ještě s poněkud menší frekvencí vede) pod článkem Recenze knihy Functional Thinking (Paradigm over syntax), zaznělo mj. i několik zajímavých názorů, které se poměrně úzce dotýkají problematiky polymorfismu, tj. poněkud zjednodušeně řečeno technologie způsobu výběru konkrétní funkce či metody, která se má v daném kontextu zavolat. Pojem „polymorfismus“ se v oblasti programovacích jazyků používá pro popis hned několika navzájem odlišných vlastností nějakého jazyka či jeho běhového prostředí, což někdy může vést ke zbytečným nedorozuměním, zvláště když se přesně nespecifikuje, který druh polymorfismu má pisatel vlastně na mysli. V mnoha programovacích jazycích podporujících objektově orientované programování (navíc založené na použití tříd) se pod pojmem „polymorfismus“ většinou automaticky myslí takzvaný podtypový polymorfismus neboli subtype polymorphism.
Podtypový polymorfismus se dále může dělit na polymorfismus statický a dynamický. U statického polymorfismu o výběru volané metody rozhoduje již překladač, který většinou do vytvářeného strojového kódu přímo umístí instrukce pro volání konkrétní funkce či metody (její adresu). Někdy se pod tento pojem poněkud nepřesně zahrnuje i přetížení funkcí. Naopak dynamický podtypový polymorfismus (v řeči OOP tedy jen „polymorfismus“) je typicky založený na pozdní vazbě – late binding – a tabulce virtuálních metod). Pokud však termín „polymorfismus“ použije vývojář používající nějaký jazyk podporující funkcionální programování, bude mít pravděpodobně na mysli polymorfismus parametrický, tedy technologii, kterou si alespoň stručně vysvětlíme v navazujících kapitolách. Aby nebylo zmatků v pojmenování málo, můžeme se setkat i s označením runtime polymorphism, což může být buď podtypový dynamický polymorfismus nebo parametrický dynamický polymorfismus :-)
2. Problém typický pro třídní OOP: je kružnice speciálním případem elipsy?
Podtypový polymorfismus pravděpodobně není nutné vývojářům, kteří tento článek čtou, podrobně představovat, protože v mainstreamových programovacích jazycích založených na objektově orientovaném programování s třídami je použit právě tento druh polymorfismu. V praxi to může znamenat, že vývojář definuje hierarchii tříd s tím, že se výběr konkrétní metody, která se má zavolat, může provést až v čase běhu aplikace, a to na základě typu objektu, jehož metoda se volá. Obecně totiž platí, že překladač nemusí mít k dispozici informaci o přesném typu objektu, a to právě na základě hierarchie tříd, kdy potomek může nahradit předka. I přes značné rozšíření podtypového polymorfismu v OOP jazycích se však v některých případech nevyhneme některým problémům, typicky problému nazývanému problém kružnice-elipsa (Circle-ellipse problem). Zkuste se nad tímto problémem sami chvíli zamyslet a zjistit, proč použití klasické třídní hierarchie a objektů s měnitelným stavem („se settery“) nutně vede k problémům:
class Ellipse implements Shape { } class Circle extends Ellipse { }
versus:
class Circle implements Shape { } class Ellipse extends Circle { }
versus:
class Circle implements Shape { } class Ellipse implements Shape { }
Ostatně velmi podobný typ problému byl popsán v již zmíněné diskusi (poněkud sofistikovanější to bude například při snaze o vytvoření hierarchie tříd číselných typů – reálná vs komplexní čísla apod.). Právě tyto kategorie problémů je možné poměrně elegantně vyřešit použitím multimetod, které nejsou svázány s hierarchií tříd.
Poznámka: tvrzení z předchozích odstavců nutně neznamená, že OOP je
ve všech případech špatný koncept; pouze by mělo být patrné, že při modelování
objektů z reálného světa musíme být velmi obezřetní k tomu, zda se ve
vytvářeném modelu neporušuje nějaký základní předpoklad, nebo zda naopak nějaký
předpoklad zbytečně nepřidáváme (u zmíněného problému kružnice-elipsa se
například základní nedostatky relativně snadno obejdou zákazem změny stavu, což
je ostatně typický přístup používaný ve funkcionálním programování).
3. Multimetody
Programovací jazyk Clojure vývojářům taktéž nabízí možnost tvorby polymorfních metod, ovšem samotný princip výběru volané metody v čase běhu aplikace nezávisí na použité třídní hierarchii: namísto běžných metod (deklarovaných typicky v rámci tříd) je totiž v Clojure možné deklarovat takzvané multimetody. Ty se skládají ze dvou částí – definice defmulti, v níž je multimetoda pojmenovaná a navíc je zde zmíněna takzvaná výběrová (dispatch) funkce, a následně z libovolného počtu definic defmethod, na něž se můžeme dívat jako na definice běžných funkcí, ovšem doplněných o kontext určující, za jakých okolností se tato konkrétní funkce tvořící část multimetody zavolá. Před popisem konkrétního způsobu zápisu multimetod si však uveďme reálný příklad, který nám ukáže, kdy je vhodné multimetody použít (v praxi však zjistíte, že se v naprosté většině případů bez multimetod a tím pádem i polymorfismu můžete obejít, minimálně v dynamicky typovaném jazyce Clojure).
4. První varianta funkce heading
Předpokládejme, že ve vyvíjené aplikaci potřebujeme vytvořit funkci (prozatím skutečně běžnou funkci) nazvanou heading. Tato funkce vypíše na standardní výstup hodnotu svého parametru, ovšem navíc tuto hodnotu od ostatních zpráv z obou stran vizuálně oddělí. První – a jak uvidíme dále, i poměrně naivní – implementace této funkce může vypadat velmi jednoduše a přímočaře. Připomeňme si jen, že *out* je objekt typu java.io.Writer, který je běžně navázaný na System.out. Můžeme tedy směle volat metodu java.io.Writer.write(), ovšem s využitím Java interop:
(defn heading [anything] (.write *out* "-----------------\n") (.write *out* anything) (.write *out* "\n-----------------\n"))
První test této funkce dopadne vcelku uspokojivě:
user=> (heading "xyzzy") ----------------- xyzzy -----------------
5. Nedostatky první verze
Zdá se, že funkce heading pracuje uspokojivě za předpokladu, že jí předáme parametr typu řetězec. Co se však stane u parametrů jiných typů? Můžeme si to jednoduše vyzkoušet:
user=> (heading 42) ----------------- * -----------------
Zde se číselná hodnota převedla na ASCII znak s kódem 42.
user=> (heading nil) ----------------- NullPointerException java.io.PrintWriter.write (PrintWriter.java:443)
Zde nejenže funkce nepracuje spolehlivě, ale navíc v programu vygenerovala výjimku.
user=> (heading [1 2 3]) ----------------- IllegalArgumentException No matching method found: write for class java.io.PrintWriter clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:80) user=> (heading {:first 1 :second 2}) ----------------- IllegalArgumentException No matching method found: write for class java.io.PrintWriter clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:80)
Pro odlišné parametry, než je řetězec či celé číslo, taktéž namísto výpisu dojde k vyhození výjimky; což pravděpodobně programátory, kteří budou naši funkci chtít použít ve svých aplikacích, moc nepotěší.
6. První vylepšení – rozlišení typu parametru předaného funkci heading
Pokusme se nyní funkci heading vylepšit, a to stále bez použití multimetod. Jedním z možných řešení (ke kterému se prosím v praxi moc často neuchylujte :-) je založeno na explicitním rozvětvení provedeného na základě zjištěného typu parametru. Využijeme zde čtveřici prediktorů nazvaných nil?, string?, number? a vector?, samozřejmě je však možné provést rozšíření i na další typy, s nimiž se v programovacím jazyce Clojure můžete setkat. Bylo by například pěkné automaticky dereferovat atomy apod.:
(defn heading [anything] (.write *out* "-----------------\n") (cond (nil? anything) (.write *out* "nothing") (string? anything) (.write *out* anything) (number? anything) (.write *out* (str anything)) (vector? anything) (.write *out* (clojure.string/join " " anything))) (.write *out* "\n-----------------\n"))
Funkce se nám prodloužila, takže se podívejme na to, jak si obstojí při testování:
user=> (heading "xyzzy") ----------------- xyzzy ----------------- user=> (heading 42) ----------------- 42 ----------------- user=> (heading nil) ----------------- nothing ----------------- user=> (heading [1 2 3]) ----------------- 1 2 3 ----------------- user=> (heading {:first 1 :second 2}) ----------------- -----------------
Vidíme, že v posledním případě, tj. při předání mapy, se mezi označené řádky nic nevypíše, což je však v pořádku, protože jsme v makru nedeklarovali žádnou větev else. Úprava je snadná:
(defn heading [anything] (.write *out* "-----------------\n") (cond (nil? anything) (.write *out* "nothing") (string? anything) (.write *out* anything) (number? anything) (.write *out* (str anything)) (vector? anything) (.write *out* (clojure.string/join " " anything)) :else (.write *out* "I'm confused")) (.write *out* "\n-----------------\n"))
7. Definice multimetody a k ní příslušné výběrové (dispatch) funkce
Výše uvedené řešení není zcela ideální, protože se v něm vlastně snažíme o ruční (ad-hoc) implementaci polymorfismu. Podívejme se tedy, jak by bylo možné využít multimetody. Víme již, že jejich definice je rozdělena minimálně do dvou částí – defmulti a defmethod. V definici defmulti je multimetoda pojmenována a navíc je zde specifikována takzvaná výběrová (dispatch) funkce:
(defmulti heading dispatch-fn)
Při volání multimetody je nejprve zavolána dispatch funkce s tím, že její návratová hodnota je následně použita pro výběr konkrétní funkce/metody, která se má zavolat (v třídním OOP, jaké je například implementováno v Javě, vlastně dispatch funkce odpovídá výrazu this). V našem konkrétním případě potřebujeme, aby dispatch funkce zjistila typ parametru a nějakým způsobem tento typ vrátila. Naivní implementace může vypadat například takto:
(defn dispatch-fn [anything] (cond (nil? anything) :nil (string? anything) :string (number? anything) :number (vector? anything) :vector))
Následuje definice jednotlivých variant multimetody. Povšimněte si, že defmethod se vlastně podobá běžné definici funkce s využitím defn, ovšem s tím rozdílem, že za jménem multimetody ještě následuje hodnota, která je při volání multimetody párována s výsledkem dispatch funkce. Toto „párování“ (dynamic dispatching) za nás provede interpret a překladač automaticky, ostatně podobně automaticky je tomu v OOP jazycích (poznámka – hodnota může být libovolná, klidně zde můžeme použít mapu, vektor, záznam atd.):
(defmethod heading :nil [val] (.write *out* "-----------------\n") (.write *out* "nothing") (.write *out* "\n-----------------\n")) (defmethod heading :string [val] (.write *out* "-----------------\n") (.write *out* val) (.write *out* "\n-----------------\n")) (defmethod heading :number [val] (.write *out* "-----------------\n") (.write *out* (str val)) (.write *out* "\n-----------------\n")) (defmethod heading :vector [val] (.write *out* "-----------------\n") (.write *out* (clojure.string/join " " val)) (.write *out* "\n-----------------\n"))
Volání multimetody se nijak neliší od volání běžné funkce (to je právě na multimetodách pěkné), takže si pusťme do testování:
user=> (heading nil) ----------------- nothing ----------------- user=> (heading "xyzzy") ----------------- xyzzy ----------------- user=> (heading 42) ----------------- 42 ----------------- user=> (heading nil) ----------------- nothing ----------------- user=> (heading [1 2 3]) ----------------- 1 2 3 -----------------
Zajímavá situace nastane ve chvíli, kdy použijeme parametr neznámého typu:
user=> (heading {:first 1 :second 2}) IllegalArgumentException No method in multimethod 'heading' for dispatch value: null clojure.lang.MultiFn.getFn (MultiFn.java:160)
I tento problém však lze snadno vyřešit, jak ostatně uvidíme v dalších kapitolách.
8. Volání multimetody na základě zjištěného typu parametru
Naši multimetodu heading je možné vylepšit, a to hned několika způsoby. Například namísto uživatelsky definované dispatch funkce můžeme v tomto případě použít vestavěnou funkci nazvanou jednoduše class, která dokáže v runtime (za běhu aplikace) detekovat typ svého argumentu. Ostatně se sami podívejme, jak tato funkce pracuje (povšimněte si, že pro nil se vrátí taktéž typ nil):
user=> (class nil) nil user=> (class "xyzzy") java.lang.String user=> (class 42) java.lang.Long user=> (class 42.0) java.lang.Double user=> (class [1 2 3]) clojure.lang.PersistentVector user=> (class (range 10)) clojure.lang.LazySeq
První část definice multimetody se nám tedy zjednoduší na jeden jediný řádek, protože nebudeme definovat žádnou dispatch funkci:
(defmulti heading class)
Druhá část definice multimetody (jednotlivé konkrétní metody) se od první verze příkladu odlišují hodnotou použitou pro spárování (dynamic dispatching):
(defmethod heading nil [val] (.write *out* "-----------------\n") (.write *out* "nothing") (.write *out* "\n-----------------\n")) (defmethod heading String [val] (.write *out* "-----------------\n") (.write *out* val) (.write *out* "\n-----------------\n")) (defmethod heading Number [val] (.write *out* "-----------------\n") (.write *out* (str val)) (.write *out* "\n-----------------\n")) (defmethod heading clojure.lang.IPersistentVector [val] (.write *out* "-----------------\n") (.write *out* (clojure.string/join " " val)) (.write *out* "\n-----------------\n"))
Opět si novou variantu multimetody otestujme:
user=> (heading nil) ----------------- nothing ----------------- user=> (heading "xyzzy") ----------------- xyzzy ----------------- user=> (heading 42) ----------------- 42 ----------------- user=> (heading nil) ----------------- nothing ----------------- user=> (heading [1 2 3]) ----------------- 1 2 3 -----------------
Vše vypadá (zdánlivě) v pořádku!
9. Implicitní metoda
Když jsme se snažili o implementaci ad-hoc polymorfismu s využitím makra cond, bylo možné použít větev else pro ty případy, pro které neexistovala speciální větev. Podobnou vlastnost podporují i multimetody, a to při použití hodnoty :default. Pokud naši multimetodu z předchozí kapitoly rozšíříme o další metodu ve tvaru:
(defmethod heading :default [anything] (.write *out* "-----------------\n") (.write *out* "I'm confused!") (.write *out* "\n-----------------\n"))
…budeme moci bez problémů metodu zavolat a předat jí množinu, mapu apod.:
user=> (heading {:first 1 :second 2}) ----------------- I'm confused! -----------------
Pokuste se o něco podobného například v Javě…
10. Hierarchie typů a způsob řešení „kolizí“
Ve skutečnosti nám však ještě zbývá vyřešit minimálně jeden problém. Řekněme, že požadavek na multimetodu heading byl rozšířen – pokud se jí předá vektor, má být vypsán v hranatých závorkách, pokud sekvence nebo jiná kolekce, pak se mají použít závorky kulaté. Zdánlivě se nejedná o nic složitého, takže by řešení mohlo vypadat třeba takto:
(defmulti heading class) (defmethod heading nil [val] (.write *out* "-----------------\n") (.write *out* "nothing") (.write *out* "\n-----------------\n")) (defmethod heading String [val] (.write *out* "-----------------\n") (.write *out* val) (.write *out* "\n-----------------\n")) (defmethod heading Number [val] (.write *out* "-----------------\n") (.write *out* (str val)) (.write *out* "\n-----------------\n")) (defmethod heading clojure.lang.IPersistentVector [val] (.write *out* "-----------------\n") (.write *out* "[") (.write *out* (clojure.string/join " " val)) (.write *out* "]") (.write *out* "\n-----------------\n")) (defmethod heading java.util.Collection [val] (.write *out* "-----------------\n") (.write *out* "(") (.write *out* (clojure.string/join " " val)) (.write *out* ")") (.write *out* "\n-----------------\n")) (defmethod heading :default [anything] (.write *out* "-----------------\n") (.write *out* "I'm confused!") (.write *out* "\n-----------------\n"))
První testování však nedopadne příliš dobře:
(heading [1 2 3]) user=> (heading [1 2 3]) IllegalArgumentException Multiple methods in multimethod 'heading' match dispatch value: class clojure.lang.PersistentVector -> interface java.util.Collection and interface clojure.lang.IPersistentVector, and neither is preferred clojure.lang.MultiFn.findAndCacheBestMethod (MultiFn.java:182)
Runtime programovacího jazyka Clojure nám celkem jasně sděluje, že pro daný typ (vektor) našel hned dvě metody, které může použít. A vzhledem k tomu, že se v multimetodách nepoužívá (a ani nemůže používat) hierarchie tříd, je runtime zmatený a namísto hádání, která metoda je danému typu „nejblíže“, raději vyhodí výjimku.
11. Programátorem zvolená preference metod
Vzhledem k tomu, jakou volnost mají programátoři při konstrukci multimetod, nás pravděpodobně příliš nepřekvapí, že i preference jednotlivých metod tvořících multimetodu je volitelná. Používá se zde deklarace prefer-method, které se musí předat jak identifikátor multimetody (heading), tak i specifikace, která metoda (resp. přesněji řečeno která hodnota vrácená dispatch funkcí) bude mít přednost. V tomto máme jasno – pokud je multimetodě předán vektor, nechť je vypsán jako vektor ve hranatých závorkách, zatímco ostatní kolekce nechť jsou vypsány v kulatých závorkách:
(prefer-method heading clojure.lang.IPersistentVector java.util.Collection)
Vše si samozřejmě otestujeme:
user=> (heading [1 2 3]) ----------------- [1 2 3] ----------------- user=> (heading '(1 2 3)) ----------------- (1 2 3) ----------------- user=> (heading (range 10)) ----------------- (0 1 2 3 4 5 6 7 8 9) -----------------
Poznámka: v deklaraci prefer-method se vždy
specifikují jen dvě hodnoty, přičemž první hodnota je preferována, ovšem nic
nám nebrání použít více těchto deklarací a vytvořit si tak vlastní
„hierarchii“
12. Finální varianta multimetody heading
Na závěr tohoto článku si ještě uveďme úplnou definici multimetody heading. Namísto uživatelsky definované dispatch funkce je použita funkce class implementovaná v základní knihovně, multimetoda správně (bez vyhození výjimky) reaguje na neznámý typ parametru a u vektorů je jasně rozlišeno, která část multimetody se má zavolat:
(defmulti heading class) (defmethod heading nil [value] (.write *out* "-----------------\n") (.write *out* "nothing") (.write *out* "\n-----------------\n")) (defmethod heading String [value] (.write *out* "-----------------\n") (.write *out* value) (.write *out* "\n-----------------\n")) (defmethod heading Number [value] (.write *out* "-----------------\n") (.write *out* (str value)) (.write *out* "\n-----------------\n")) (defmethod heading clojure.lang.IPersistentVector [value] (.write *out* "-----------------\n") (.write *out* "[") (.write *out* (clojure.string/join " " value)) (.write *out* "]") (.write *out* "\n-----------------\n")) (defmethod heading java.util.Collection [value] (.write *out* "-----------------\n") (.write *out* "(") (.write *out* (clojure.string/join " " value)) (.write *out* ")") (.write *out* "\n-----------------\n")) (defmethod heading :default [anything] (.write *out* "-----------------\n") (.write *out* "I'm confused!") (.write *out* "\n-----------------\n")) (prefer-method heading clojure.lang.IPersistentVector java.util.Collection)
13. Odkazy na předchozí části tohoto seriálu
- 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/ - 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/ - 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 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/
14. Odkazy na Internetu
- Runtime Polymorphism
http://www.clojure.org/about/runtime_polymorphism - Multiple dispatch
https://en.wikipedia.org/wiki/Multiple_dispatch - Dynamic dispatch
https://en.wikipedia.org/wiki/Dynamic_dispatch#Single_and_multiple_dispatch - Dynamický výběr
https://cs.wikipedia.org/wiki/Dynamick%C3%BD_v%C3%BDb%C4%9Br - Back to the Future: Lisp as a Base for a Statistical Computing System
https://www.stat.auckland.ac.nz/~ihaka/downloads/Compstat-2008.pdf - gg4clj: a simple wrapper for using R's ggplot2 in Clojure and Gorilla REPL
https://github.com/JonyEpsilon/gg4clj - Analemma: a Clojure-based SVG DSL and charting library
http://liebke.github.io/analemma/ - Clojupyter: a Jupyter kernel for Clojure
https://github.com/roryk/clojupyter - Communicating sequential processes
https://en.wikipedia.org/wiki/Communicating_sequential_processes - The IPython Notebook
http://ipython.org/notebook.html - Jypyter: open source, interactive data science and scientific computing across over 40 programming languages
https://jupyter.org/ - nbviewer: a simple way to share Jupyter Notebooks
https://nbviewer.jupyter.org/ - Clojure core.async
http://www.infoq.com/presentations/clojure-core-async - core.async API Reference
https://clojure.github.io/core.async/ - Clojure core.async Channels
http://clojure.com/blog/2013/06/28/clojure-core-async-channels.html - core.async examples
https://github.com/clojure/core.async/blob/master/examples/walkthrough.clj - Timothy Baldridge – Core.Async
https://www.youtube.com/watch?v=enwIIGzhahw - Designing Front End Applications with core.async
http://go.cognitect.com/core_async_webinar_recording - Mastering Concurrent Processes with core.async
http://www.braveclojure.com/core-async/ - LispCast: Clojure core.async
https://www.youtube.com/watch?v=msv8Fvtd6YQ - Julian Gamble – Applying the paradigms of core.async in ClojureScript
https://www.youtube.com/watch?v=JUrOebC5HmA - Zip archiv s Clojure 1.8.0
http://repo1.maven.org/maven2/org/clojure/clojure/1.8.0/clojure-1.8.0.zip - Clojure 1.8 is now available
http://clojure.org/news/2016/01/19/clojure18 - Changes to Clojure in Version 1.8
https://github.com/clojure/clojure/blob/master/changes.md - Socket Server REPL
http://dev.clojure.org/display/design/Socket+Server+REPL - CLJ-1671: Clojure socket server
http://dev.clojure.org/jira/browse/CLJ-1671 - CLJ-1449: Add clojure.string functions for portability to ClojureScript
http://dev.clojure.org/jira/browse/CLJ-1449 - Launching a Socket Server
http://clojure.org/reference/repl_and_main#_launching_a_socket_server - API for clojure.string
http://clojure.github.io/clojure/branch-master/clojure.string-api.html - Clojars:
https://clojars.org/ - Seznam knihoven na Clojars:
https://clojars.org/projects - Clojure Cookbook: Templating HTML with Enlive
https://github.com/clojure-cookbook/clojure-cookbook/blob/master/07_webapps/7–11_enlive.asciidoc - An Introduction to Enlive
https://github.com/swannodette/enlive-tutorial/ - Enlive na GitHubu
https://github.com/cgrand/enlive - Expectations: příklady atd.
http://jayfields.com/expectations/ - Expectations na GitHubu
https://github.com/jaycfields/expectations - Lein-expectations na GitHubu
https://github.com/gar3thjon3s/lein-expectations - Testing Clojure With Expectations
https://semaphoreci.com/blog/2014/09/23/testing-clojure-with-expectations.html - Clojure testing TDD/BDD libraries: clojure.test vs Midje vs Expectations vs Speclj
https://www.reddit.com/r/Clojure/comments/1viilt/clojure_testing_tddbdd_libraries_clojuretest_vs/ - Testing: One assertion per test
http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html - Rewriting Your Test Suite in Clojure in 24 hours
http://blog.circleci.com/rewriting-your-test-suite-in-clojure-in-24-hours/ - Clojure doc: zipper
http://clojuredocs.org/clojure.zip/zipper - Clojure doc: parse
http://clojuredocs.org/clojure.xml/parse - Clojure doc: xml-zip
http://clojuredocs.org/clojure.zip/xml-zip - Clojure doc: xml-seq
http://clojuredocs.org/clojure.core/xml-seq - Parsing XML in Clojure
https://github.com/clojuredocs/guides - Clojure Zipper Over Nested Vector
https://vitalyper.wordpress.com/2010/11/23/clojure-zipper-over-nested-vector/ - Understanding Clojure's PersistentVector implementation
http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation - Understanding Clojure's PersistentHashMap (deftwice…)
http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html - Assoc and Clojure's PersistentHashMap: part ii
http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html - Ideal Hashtrees (paper)
http://lampwww.epfl.ch/papers/idealhashtrees.pdf - Clojure home page
http://clojure.org/ - Clojure (downloads)
http://clojure.org/downloads - Clojure Sequences
http://clojure.org/sequences - Clojure Data Structures
http://clojure.org/data_structures - The Structure and Interpretation of Computer Programs: 2.2.1 Representing Sequences
http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-15.html#%_sec2.2.1 - The Structure and Interpretation of Computer Programs: 3.3.1 Mutable List Structure
http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-22.html#%_sec3.3.1 - 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/ - Clojure (na Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (na Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - SICP (The Structure and Interpretation of Computer Programs)
http://mitpress.mit.edu/sicp/ - Pure function
http://en.wikipedia.org/wiki/Pure_function - Funkcionální programování
http://cs.wikipedia.org/wiki/Funkcionální_programování - Čistě funkcionální (datové struktury, jazyky, programování)
http://cs.wikipedia.org/wiki/Čistě_funkcionální - Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html - Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html - Clojure Macro Tutorial (Part III: Syntax Quote)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html - Tech behind Tech: Clojure Macros Simplified
http://techbehindtech.com/2010/09/28/clojure-macros-simplified/ - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - List comprehension
http://en.wikipedia.org/wiki/List_comprehension - List Comprehensions in Clojure
http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html - Clojure Programming Concepts: List Comprehension
http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#List_Comprehension - Clojure core API: for macro
http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/for - cirrus machina – The Clojure for macro
http://www.cirrusmachina.com/blog/comment/the-clojure-for-macro/ - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - 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 - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.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/ - clojure.inspector
http://clojure.github.io/clojure/clojure.inspector-api.html - The Clojure Toolbox
http://www.clojure-toolbox.com/ - Unit Testing in Clojure
http://nakkaya.com/2009/11/18/unit-testing-in-clojure/ - Testing in Clojure (Part-1: Unit testing)
http://blog.knoldus.com/2014/03/22/testing-in-clojure-part-1-unit-testing/ - API for clojure.test – Clojure v1.6 (stable)
https://clojure.github.io/clojure/clojure.test-api.html - Leiningen: úvodní stránka
http://leiningen.org/ - Leiningen: Git repository
https://github.com/technomancy/leiningen - leiningen-win-installer
http://leiningen-win-installer.djpowell.net/ - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - Transient Data Structureshttp://clojure.org/transients