Multimetody v Clojure aneb polymorfismus bez použití OOP

Pavel Tišnovský 14. 4. 2016

Programovací jazyk Clojure obsahuje podporu pro tvorbu a volání takzvaných multimetod. Ty se odlišují především mechanismem dynamického výběru konkrétní metody v čase běhu aplikace.

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?

3. Multimetody

4. První varianta funkce heading

5. Nedostatky první verze

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

9. Implicitní metoda

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

14. Odkazy na Internetu

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:

widgety

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

  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. 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/
  35. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  36. 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/
  37. 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/
  38. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  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/

14. Odkazy na Internetu

  1. Runtime Polymorphism
    http://www.clojure.org/abou­t/runtime_polymorphism
  2. Multiple dispatch
    https://en.wikipedia.org/wi­ki/Multiple_dispatch
  3. Dynamic dispatch
    https://en.wikipedia.org/wi­ki/Dynamic_dispatch#Single_an­d_multiple_dispatch
  4. Dynamický výběr
    https://cs.wikipedia.org/wi­ki/Dynamick%C3%BD_v%C3%BDb%C4%9Br
  5. Back to the Future: Lisp as a Base for a Statistical Computing System
    https://www.stat.auckland­.ac.nz/~ihaka/downloads/Com­pstat-2008.pdf
  6. gg4clj: a simple wrapper for using R's ggplot2 in Clojure and Gorilla REPL
    https://github.com/JonyEpsilon/gg4clj
  7. Analemma: a Clojure-based SVG DSL and charting library
    http://liebke.github.io/analemma/
  8. Clojupyter: a Jupyter kernel for Clojure
    https://github.com/roryk/clojupyter
  9. Communicating sequential processes
    https://en.wikipedia.org/wi­ki/Communicating_sequenti­al_processes
  10. The IPython Notebook
    http://ipython.org/notebook.html
  11. Jypyter: open source, interactive data science and scientific computing across over 40 programming languages
    https://jupyter.org/
  12. nbviewer: a simple way to share Jupyter Notebooks
    https://nbviewer.jupyter.org/
  13. Clojure core.async
    http://www.infoq.com/presen­tations/clojure-core-async
  14. core.async API Reference
    https://clojure.github.io/core.async/
  15. Clojure core.async Channels
    http://clojure.com/blog/2013/06/28/clo­jure-core-async-channels.html
  16. core.async examples
    https://github.com/clojure/co­re.async/blob/master/exam­ples/walkthrough.clj
  17. Timothy Baldridge – Core.Async
    https://www.youtube.com/wat­ch?v=enwIIGzhahw
  18. Designing Front End Applications with core.async
    http://go.cognitect.com/co­re_async_webinar_recording
  19. Mastering Concurrent Processes with core.async
    http://www.braveclojure.com/core-async/
  20. LispCast: Clojure core.async
    https://www.youtube.com/wat­ch?v=msv8Fvtd6YQ
  21. Julian Gamble – Applying the paradigms of core.async in ClojureScript
    https://www.youtube.com/wat­ch?v=JUrOebC5HmA
  22. Zip archiv s Clojure 1.8.0
    http://repo1.maven.org/ma­ven2/org/clojure/clojure/1­.8.0/clojure-1.8.0.zip
  23. Clojure 1.8 is now available
    http://clojure.org/news/2016/01/19/clo­jure18
  24. Changes to Clojure in Version 1.8
    https://github.com/clojure/clo­jure/blob/master/changes.md
  25. Socket Server REPL
    http://dev.clojure.org/dis­play/design/Socket+Server+REPL
  26. CLJ-1671: Clojure socket server
    http://dev.clojure.org/jira/browse/CLJ-1671
  27. CLJ-1449: Add clojure.string functions for portability to ClojureScript
    http://dev.clojure.org/jira/browse/CLJ-1449
  28. Launching a Socket Server
    http://clojure.org/referen­ce/repl_and_main#_launchin­g_a_socket_server
  29. API for clojure.string
    http://clojure.github.io/clo­jure/branch-master/clojure.string-api.html
  30. Clojars:
    https://clojars.org/
  31. Seznam knihoven na Clojars:
    https://clojars.org/projects
  32. Clojure Cookbook: Templating HTML with Enlive
    https://github.com/clojure-cookbook/clojure-cookbook/blob/master/07_webapps/7–11_enlive.asciidoc
  33. An Introduction to Enlive
    https://github.com/swannodette/enlive-tutorial/
  34. Enlive na GitHubu
    https://github.com/cgrand/enlive
  35. Expectations: příklady atd.
    http://jayfields.com/expectations/
  36. Expectations na GitHubu
    https://github.com/jaycfi­elds/expectations
  37. Lein-expectations na GitHubu
    https://github.com/gar3thjon3s/lein-expectations
  38. Testing Clojure With Expectations
    https://semaphoreci.com/blog/2014/09/23/tes­ting-clojure-with-expectations.html
  39. Clojure testing TDD/BDD libraries: clojure.test vs Midje vs Expectations vs Speclj
    https://www.reddit.com/r/Clo­jure/comments/1viilt/cloju­re_testing_tddbdd_librari­es_clojuretest_vs/
  40. Testing: One assertion per test
    http://blog.jayfields.com/2007/06/tes­ting-one-assertion-per-test.html
  41. Rewriting Your Test Suite in Clojure in 24 hours
    http://blog.circleci.com/rewriting-your-test-suite-in-clojure-in-24-hours/
  42. Clojure doc: zipper
    http://clojuredocs.org/clo­jure.zip/zipper
  43. Clojure doc: parse
    http://clojuredocs.org/clo­jure.xml/parse
  44. Clojure doc: xml-zip
    http://clojuredocs.org/clojure.zip/xml-zip
  45. Clojure doc: xml-seq
    http://clojuredocs.org/clo­jure.core/xml-seq
  46. Parsing XML in Clojure
    https://github.com/clojuredocs/guides
  47. Clojure Zipper Over Nested Vector
    https://vitalyper.wordpres­s.com/2010/11/23/clojure-zipper-over-nested-vector/
  48. Understanding Clojure's PersistentVector implementation
    http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation
  49. Understanding Clojure's PersistentHashMap (deftwice…)
    http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html
  50. Assoc and Clojure's PersistentHashMap: part ii
    http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html
  51. Ideal Hashtrees (paper)
    http://lampwww.epfl.ch/pa­pers/idealhashtrees.pdf
  52. Clojure home page
    http://clojure.org/
  53. Clojure (downloads)
    http://clojure.org/downloads
  54. Clojure Sequences
    http://clojure.org/sequences
  55. Clojure Data Structures
    http://clojure.org/data_structures
  56. 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
  57. 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
  58. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  59. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  60. 4Clojure
    http://www.4clojure.com/
  61. ClojureDoc (rozcestník s dokumentací jazyka Clojure)
    http://clojuredocs.org/
  62. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  63. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  64. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  65. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  66. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  67. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  68. 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
  69. Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html
  70. Clojure Macro Tutorial (Part III: Syntax Quote)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html
  71. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  72. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  73. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  74. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  75. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  76. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  77. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  78. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  79. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  80. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  81. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  82. Threading macro (dokumentace k jazyku Clojure)
    https://clojure.github.io/clo­jure/clojure.core-api.html#clojure.core/->
  83. Understanding the Clojure → macro
    http://blog.fogus.me/2009/09/04/un­derstanding-the-clojure-macro/
  84. clojure.inspector
    http://clojure.github.io/clo­jure/clojure.inspector-api.html
  85. The Clojure Toolbox
    http://www.clojure-toolbox.com/
  86. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  87. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  88. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  89. Leiningen: úvodní stránka
    http://leiningen.org/
  90. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  91. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  92. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  93. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  94. Clojure.org: Atoms
    http://clojure.org/Atoms
  95. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  96. Transient Data Structureshttp://clojure.or­g/transients
Našli jste v článku chybu?
120na80.cz: Co je padesátkrát sladší než cukr?

Co je padesátkrát sladší než cukr?

DigiZone.cz: Sony MP-CL1A: miniaturní projektor

Sony MP-CL1A: miniaturní projektor

DigiZone.cz: Nova opět stahuje „milionáře“

Nova opět stahuje „milionáře“

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

Lupa.cz: Patička e-mailu závazná jako vlastnoruční podpis?

Patička e-mailu závazná jako vlastnoruční podpis?

Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

Podnikatel.cz: Takhle se prodávají mražené potraviny

Takhle se prodávají mražené potraviny

Podnikatel.cz: EET pro e-shopy? Postavené na hlavu

EET pro e-shopy? Postavené na hlavu

DigiZone.cz: Technisat připravuje trojici DAB

Technisat připravuje trojici DAB

Podnikatel.cz: Byla finanční manažerka, teď cvičí jógu

Byla finanční manažerka, teď cvičí jógu

DigiZone.cz: Digi Slovakia zařazuje stanice SPI

Digi Slovakia zařazuje stanice SPI

Vitalia.cz: Tohle jsou nejlepší česká piva podle odborníků

Tohle jsou nejlepší česká piva podle odborníků

Vitalia.cz: Voda z Vltavy před a po úpravě na pitnou

Voda z Vltavy před a po úpravě na pitnou

Vitalia.cz: Jsou vegani a vyrábějí nemléko

Jsou vegani a vyrábějí nemléko

DigiZone.cz: Funbox 4K v DVB-T2 má ostrý provoz

Funbox 4K v DVB-T2 má ostrý provoz

DigiZone.cz: Na jaká videa se vlastně díváme

Na jaká videa se vlastně díváme

DigiZone.cz: Rapl: seriál, který vás smíří s ČT

Rapl: seriál, který vás smíří s ČT

Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

Vitalia.cz: Muž, který miluje příliš. Ženám neimponuje

Muž, který miluje příliš. Ženám neimponuje