Hlavní navigace

Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure

Pavel Tišnovský

Další užitečnou knihovnou použitelnou vývojáři, kteří pro tvorbu aplikací využívají programovací jazyk Clojure, je knihovna nazvaná Seesaw. Umožňuje snadnou tvorbu grafického rozhraní, přičemž zde nalezneme všechny důležité prvky jazyka Clojure, zejména anonymní funkce, ekvivalence zápisu kódu a dat atd.

Obsah

1. Seesaw: knihovna pro snadnou tvorbu GUI v programovacím jazyce Clojure

2. Koncepty, na nichž je knihovna Seesaw postavena

3. První demonstrační příklad: okno s tlačítkem

4. Druhý demonstrační příklad: vylepšení zdrojového kódu s použitím threading makra

5. Alternativní způsob přidání tlačítka do okna

6. Zdrojový kód třetího demonstračního příkladu

7. Zajištění automatického ukončení aplikace po zavření hlavního okna

8. Listenery a (anonymní) callback funkce reagující na události

9. Zdrojový kód čtvrtého demonstračního příkladu

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

11. Odkazy na předchozí části seriálu

12. Odkazy na Internetu

1. Seesaw: knihovna pro snadnou tvorbu GUI v programovacím jazyce Clojure

Další zajímavou a pro některé typy aplikací i velmi užitečnou knihovnou použitelnou vývojáři, kteří tvoří své aplikace v programovacím jazyku Clojure, je knihovna nazvaná Seesaw. Tato knihovna je určena pro tvorbu grafického uživatelského rozhraní a jak uvidíme z demonstračních příkladů, je použití této knihovny poměrně jednoduché a přímočaré. Je tomu tak zejména z toho důvodu, že programovací jazyk Clojure má několik vlastností, které jsou velice dobře využitelné právě pro tvorbu grafického uživatelského prostředí. Jedná se zejména o ekvivalenci zápisu programového kódu a dat (homoikonicitu), existenci anonymních funkcí, možnost použití uzávěrů apod. Tyto vlastnosti se projeví jak při deklarativním způsobu zápisu designu jednotlivých oken a formulářů, tak i při programování reakcí na události (events) vznikající při interakci aplikace s uživatelem s využitím grafického uživatelského rozhraní (zapomenout nesmíme ani na velmi snadné volání asynchronně běžících funkcí).

Vzhledem k tomu, že programovací jazyk Clojure běží nad virtuálním strojem jazyka Java, používá knihovna Seesaw interně známou a mnohými programátory proklínanou :-) knihovnu Swing. Ovšem rozhraní Seesaw nemá prakticky nic společného s těžkopádným a mnohými vývojáři špatně používaným Swingem, dokonce je možné v Seesaw vytvářet plnohodnotné aplikace bez znalosti Swingu (mj. právě kvůli nekorektnímu způsobu používání Swingu má tato knihovna a vůbec GUI psané v Javě tak špatnou pověst, to je však z dosti velké části problém skrytý již v samotném návrhu této knihovny). Ve skutečnosti má programátorský model představený knihovnou Seesaw nejblíže ke známému duu Tcl/Tk, ovšem s tím rozdílem, že se namísto dnes již poněkud zastaralého jazyka Tcl používá modernější a lépe navržený jazyk Clojure (zde je nutné dodat, že ten je postavený na 57 let starých myšlenkách).

2. Koncepty, na nichž je knihovna Seesaw postavena

Při návrhu knihovny Seesaw bylo nutné propojit dva rozdílné světy: svět funkcionálního jazyka Clojure, v němž je kladen velký důraz na použití neměnných dat a datových struktur, a svět grafického uživatelského rozhraní, kde naopak každý prvek GUI má svůj stav, který se často mění, většinou v reakci na akce prováděné uživatelem. Tvůrci knihovny Seesaw tento problém vyřešili následovně – všechny prvky grafického uživatelského rozhraní se vytváří s využitím funkcí („konstruktorů“), přičemž tyto funkce mají proměnný počet pojmenovaných parametrů, což znamená, že před hodnotou každého parametru je použit keyword, tj. symbol, před jehož jménem je zapsána dvojtečka. Přesně tímto způsobem se s prvky grafického uživatelského rozhraní pracuje v knihovně Tk. Podívejme se na jednoduchý úryvek zdrojového kódu, v němž jsou vytvořeny dvě lokální proměnné, přičemž první proměnná představuje okno (rámec) a druhá proměnná tlačítko:

(let [main-frame (frame :title "Hello world!")
      btn        (button :text "Click Me"
                         :mnemonic \C
                         :background "#afa")]

Dále v knihovně Seesaw existují funkce, které mění stav prvků grafického uživatelského rozhraní. Jména všech těchto funkcí vždy končí vykřičníkem, což je konvence používaná i v mnoha dalších knihovnách a samozřejmě i ve standardní knihovně samotného jazyka Clojure – vykřičník značí, že se uvnitř funkce mění stav nějakého objektu. Změnou prvku GUI je myšleno jeho zobrazení, změna velikosti, přidání dalšího prvku na jeho plochu (pokud se jedná o kontejner) atd. Opět se podívejme na ilustrační příklad:

; přidání tlačítka na kontejner (rámec)
(config! main-frame :content btn)
 
; změna velikosti rámce
(pack!   main-frame)
 
; zobrazení rámce (a samozřejmě i tlačítka na něm)
(show!   main-frame)

Navíc všechny funkce měnící stav GUI prvku vrací jako svou návratovou hodnotu daný prvek, čehož je možné využít, a to díky existenci threading makra zmíněného v dalších kapitolách.

Podrobnější popis všech pěti výše popsaných funkcí můžete nalézt v oficiální dokumentaci ke knihovně Seesaw:

# Funkce
1 frame
2 button
3 config!
4 pack!
5 show!

3. První demonstrační příklad: okno s tlačítkem

Podívejme se nyní na způsob vytvoření jednoduché aplikace, která po svém spuštění zobrazí okno a v něm tlačítko. Kostru aplikace vytvoříme nám již známým způsobem s využitím nástroje Leiningen:

lein new app seesaw1

Po spuštění tohoto příkazu by se měla vygenerovat následující struktura adresářů a souborů:

.
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── resources
├── src
│   └── seesaw1
│       └── core.clj
└── test
    └── seesaw1
        └── core_test.clj

Projektový soubor project.clj je nutné nepatrně upravit – vektor uložený pod klíčem :dependencies musí obsahovat i prvek se jménem a verzí knihovny Seesaw:

(defproject seesaw1 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [seesaw "1.4.5"]]
  :main ^:skip-aot seesaw1.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Stažení všech potřebných závislostí, tj. jak knihovny Seesaw, tak i dalších knihoven, které jsou pro korektní práci Seesaw potřebné, vyřeší příkaz:

lein deps

Následuje úprava obsahu souboru src/seesaw1/core.clj. Zde je deklarována jen jediná funkce nazvaná -main, v níž je vytvořeno okno s titulkem a do tohoto okna je ihned poté přidáno tlačítko. Následně je zavolána funkce pack!, která upraví velikost okna takovým způsobem, aby se do něho tlačítko vešlo a poslední příkaz show! zajistí zobrazení okna:

(ns seesaw1.core
    (:gen-class))
 
(use 'seesaw.core)
 
(defn -main
    [& args]
    (let [main-frame (frame :title "Hello world!")
          btn        (button :text "Click Me")]
          (config! main-frame :content btn)
          (pack!   main-frame)
          (show!   main-frame)))

Tuto velmi jednoduchou aplikaci je možné spustit příkazem:

lein run

Ukončení se provede stiskem Ctrl+C z terminálu.

Při použití „čistého“ Swingu dojdeme k mnohem horšímu programu, v němž se pouze přepisuje logika, kterou by měla podobná aplikace naprogramovaná v Javě:

(ns swingapp.core
  (:gen-class))
 
(import 'javax.swing.JFrame)
(import 'javax.swing.JButton)
 
(defn -main
    [& args]
    (doto (JFrame. "Hello world!")
          (.add (doto (JButton. "Click Me")))
          .pack
          (.setVisible true)))

4. Druhý demonstrační příklad: vylepšení zdrojového kódu s použitím threading makra

Již ve druhé kapitole jsme se zmínili o tom, že většina funkcí měnících stav prvku grafického uživatelského prostředí vrací změněný prvek jako svou návratovou hodnotu. To mj. znamená, že volání takových funkcí je možné zřetězit a použít takzvané threading makro. Namísto zápisu:

(config! main-frame :content btn)
(pack!   main-frame)
(show!   main-frame)))

který připomíná volání setterů (popř. i dalších mutátorů) v objektově orientovaných programovacích jazycích, je vhodnější použít threading makro ve stylu:

(-> main-frame
    (config! :content btn)
    (pack!)
    (show!))))

Takto je možné zřetězit libovolný počet funkcí, přičemž threading makro zajistí, že hodnota uložená v lokální proměnné main-frame bude do funkcí config!, pack!show! předána jako první (a mnohdy i jediný) parametr (ve skutečnosti je do dalších funkcí předána návratová hodnota funkce předchozí, což je však v tomto případě reference na ten samý prvek). Tímto způsobem, který by měl být pro aplikace využívající knihovnu Seesaw idiomatický, byl upraven zdrojový kód prvního demonstračního příkladu a vznikl příklad druhý se jménem seesaw2.

Vytvoření kostry příkladu:

lein new app seesaw2

Vzniknout by měla tato adresářová struktura:

.
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── resources
├── src
│   └── seesaw2
│       └── core.clj
└── test
    └── seesaw2
        └── core_test.clj

Obsah projektového souboru project.clj:

(defproject seesaw2 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [seesaw "1.4.5"]]
  :main ^:skip-aot seesaw2.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Obsah souboru core.clj:

(ns seesaw2.core
    (:gen-class))
 
(use 'seesaw.core)
 
(defn -main
    [& args]
    (let [main-frame (frame :title "Hello world!")
          btn        (button :text "Click Me")]
          (-> main-frame
              (config! :content btn)
              (pack!)
              (show!))))

Threading makro je skutečně velmi užitečné, zejména protože zvyšuje čitelnost zdrojového kódu. Proto ho budeme často používat i v dalších demonstračních příkladech.

5. Alternativní způsob přidání tlačítka do okna

K oběma již popsaným způsobům vytvoření okna (rámce) a přidání tlačítka do tohoto okna můžeme přidat ještě třetí způsob, který je podle mého názoru nejsnáze použitelný. Nejprve si však zopakujme tu část druhého demonstračního příkladu, kde dochází k vytvoření tlačítka a přidání tohoto tlačítka na plochu rámce:

(let [main-frame (frame :title "Hello world!")
      btn        (button :text "Click Me")]
      (-> main-frame
          (config! :content btn)

Nejdůležitější je v tuto chvíli poslední řádek, v němž se mění vlastnost „content“ rámce. Tuto vlastnost však ve skutečnosti můžeme nastavit i ve chvíli, kdy je okno vytvářeno, ovšem za předpokladu, že již existuje objekt představující tlačítko. Alternativně lze tedy okno vytvořit takto:

(frame :title "Hello world!"
       :content (button :text "Click me"))

Pokud je zapotřebí přistupovat i k objektu představujícího tlačítko (což skutečně potřebujeme, například při konfiguraci listenerů), použije se mírně modifikovaný, stále však úsporně zapsaný kód:

(let [btn (button :text "Click me")]
     (-> (frame :title "Hello world!"
                :content btn)
            ...
            další funkce přistupující k oknu/rámci
            ...
            ...))

Užitečný je taktéž fakt, že celý zápis té části programového kódu, která se zabývá vytvořením prvků grafického uživatelského rozhraní, vlastně může být jednoduše generován například RAD nástroji. Odpadá tak nutnost použití jakéhokoli mezikódu, například uložení designu formuláře do souborů typu XML (což sice může být v některých případech užitečné, zde by se však jednalo o pověstný kanón na vrabce).

6. Zdrojový kód třetího demonstračního příkladu

Podívejme se nyní na praktické využití znalostí, s nimiž jsme se seznámili v páté kapitole. I v dnešním třetím demonstračním příkladu se po jeho spuštění zobrazí okno obsahující jediné tlačítko, ovšem při vytváření okna se nastaví jeho obsah, tj. změní se vlastnost „content“. Vytvoření kostry příkladu se provede stejným příkazem, jako tomu bylo i u předchozích dvou demonstračních příkladů:

lein new app seesaw3

Vzniknout by měla tato adresářová struktura, která se samozřejmě nebude odlišovat od struktury, kterou jsme již viděli u obou předchozích demonstračních příkladů:

.
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── resources
├── src
│   └── seesaw3
│       └── core.clj
└── test
    └── seesaw3
        └── core_test.clj

Obsah projektového souboru project.clj je opět důvěrně známý:

(defproject seesaw3 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [seesaw "1.4.5"]]
  :main ^:skip-aot seesaw3.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Obsah souboru core.clj:

(ns seesaw3.core
    (:gen-class))
 
(use 'seesaw.core)
 
(defn -main
    [& args]
    (-> (frame :title "Hello world!"
               :content (button :text "Click me"))
        (pack!)
        (show!)))

Jak je ze zdrojového kódu patrné, je tlačítko ihned při svém vytvoření uloženo do vlastnosti „content“ svého nadřazeného rámce. Připomeňme si, že okna (rámce) patří v knihovně Swing mezi takzvané kontejnery a právě proto je možné na jejich plochu vkládat další prvky grafického uživatelského rozhraní.

7. Zajištění automatického ukončení aplikace po zavření hlavního okna

Tato kapitola bude velmi krátká, protože si zde pouze ukážeme, jakým způsobem lze zajistit, že se celá aplikace korektně ukončí ve chvíli, kdy uživatel zavře její hlavní (a v tuto chvíli vlastně i jediné) okno. Předchozí tři demonstrační příklady totiž i po zavření hlavního okna stále běžely a bylo je nutné ukončit z terminálu pomocí CTRL+C či příkazem kill, což však v žádném případě není chování, které uživatelé od GUI aplikace očekávají. Korektního chování lze docílit různými způsoby, nejjednodušší je však změnit vlastnost „on-close“ hlavního okna. Původní programový kód vypadal zhruba takto:

(defn -main
    [& args]
    (let [btn (button :text "Click me")]
         (-> (frame :title "Hello world!"
                    :content btn)
             (pack!)
             (show!))))

Nový kód je prakticky stejný, ovšem nastavuje již zmíněnou vlastnost „on-close“ a to konkrétně na speciální hodnotu :exit:

(defn -main
    [& args]
    (let [btn (button :text "Click me")]
         (-> (frame :title "Hello world!"
                    :on-close :exit
                    :content btn)
             (pack!)
             (show!))))

Teoreticky by bylo možné zavolat metodu setDefaultCloseOperation(E­XIT_ON_CLOSE), tuto možnost ovšem necháme spíše programátorům, kteří musí programovat přímo v Javě :-)

8. Listenery a (anonymní) callback funkce reagující na události

Při programování aplikace s grafickým uživatelským rozhraním je nutné zajistit interakci uživatele s touto aplikací. Jinými slovy to znamená, že aplikace musí adekvátním způsobem reagovat na všechny akce prováděné uživatelem, přičemž tyto akce obecně nastávají asynchronně s během aplikace. Korektní naprogramování reakce na události (events) je při tvorbě GUI aplikací kritická a možná i z tohoto důvodu se můžeme setkat hned s několika přístupy ke zpracování událostí. Některé GUI knihovny jsou založeny na klasické smyčce událostí (event loop), která mnohdy obsahuje obří rozvětvení (case) s obsluhou jednotlivých typů událostí. Tohoto způsobu se drží například SDL nebo původní WinAPI.

Alternativní způsob nabízí použití systému signálů a slotů popř. registrace callback funkcí zavolaných v případě vzniku události. V jazyce Java, kde nelze (či přesněji řečeno donedávna nešlo) vytvořit plnohodnotnou callback funkci a předat odkaz na funkci dalšímu kódu, se namísto toho používaly anonymní třídy implementující nějaké rozhraní představující handler události. V knihovně Seesaw je použit již zmíněný mnohem elegantnější systém callback funkcí, které lze zaregistrovat pro každou událost zvlášť. Mnohdy jsou callback funkce realizovány anonymními funkcemi.

Nejprve se opět podívejme na příklad, který již známe z předchozích kapitol:

(let [btn (button :text "Click me"
                  :background "#afa")]
     (-> (frame :title "Hello world!"
                :on-close :exit
                :content btn)
         (pack!)
         (show!))))

Do tohoto programového kódu budeme chtít přidat reakci na různé události, které mohou nastat ve chvíli, kdy uživatel stiskne tlačítko btn, kdy najede kurzorem myši na toto tlačítko či kdy naopak kurzor myši plochu tlačítka opustí. V knihovně Seesaw je možné s každou událostí spojit zvolenou funkci s využitím funkce listen, které se předá objekt představující prvek grafického uživatelského rozhraní a následně libovolný počet dvojic, kde první prvek dvojice představuje typ události (:mouse-clicked atd.) a druhý prvek pak funkci, která se má zavolat ve chvíli, kdy k události dojde. V našem jednoduchém případě si vystačíme s anonymními funkcemi deklarovanými s využitím fn (a nikoli defn):

(let [btn (button :text "Click me"
                  :background "#afa")]
     (listen btn :mouse-clicked (fn [e] (println "Very well"))
                 :mouse-entered (fn [e] (println "Mouse caught"))
                 :mouse-exited  (fn [e] (println "Mouse escaped")))
     (-> (frame :title "Hello world!"
                :on-close :exit
                :content btn)
         (pack!)
         (show!))))

Poznámka – zde není možné použít alternativní způsob deklarace anonymní funkce s využitím makra #(), a to z toho důvodu, že překladač musí dopředu znát počet parametrů volané funkce. To je jeden z negativních důsledků použití rozhraní mezi Clojure a Javou. Pokud by se interně nevyužívala knihovna Swing, nebylo by využití makra #() problematické.

9. Zdrojový kód čtvrtého demonstračního příkladu

Kostru dnešního čtvrtého a současně i posledního demonstračního příkladu, v němž jsou použity výše popsané listenery, vytvoříme příkazem:

lein new app seesaw4

Vznikne nám již známá adresářová struktura:

.
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── resources
├── src
│   └── seesaw4
│       └── core.clj
└── test
    └── seesaw4
        └── core_test.clj

Obsah projektového souboru project.clj je opět důvěrně známý:

(defproject seesaw4 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [seesaw "1.4.5"]]
  :main ^:skip-aot seesaw4.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Obsah souboru core.clj:

(ns seesaw4.core
    (:gen-class))
 
(use 'seesaw.core)
 
(defn -main
    [& args]
    (let [btn (button :text "Click me"
                      :background "#afa")]
         (listen btn :mouse-clicked (fn [e] (println "Very well"))
                     :mouse-entered (fn [e] (println "Mouse caught"))
                     :mouse-exited  (fn [e] (println "Mouse escaped")))
         (-> (frame :title "Hello world!"
                    :on-close :exit
                    :content btn)
             (pack!)
             (show!))))

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

Všechny čtyři dnes popsané demonstrační příklady byly, podobně jako v předchozích částech tohoto seriálu, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/clojure-examples. V tabulce zobrazené pod tímto odstavcem naleznete na jednotlivé příklady přímé odkazy. Přidán byl i pátý příklad používající knihovnu Swing (a tím pádem i poměrně velké množství neidiomatických konstrukcí):

11. Odkazy na předchozí části seriálu

Stalo se již zvykem uvést odkazy na všechny předchozí části tohoto seriálu:

  1. Leiningen: nástroj pro správu projektů napsaných v Clojure
    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 (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  3. 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/
  4. 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/
  5. 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/
  6. 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/
  7. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  8. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  9. 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/
  10. 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-2/
  11. 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/
  12. 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/
  13. 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/

12. Odkazy na Internetu

  1. Building User Interfaces with Seesaw (slajdy k přednášce)
    http://darevay.com/talks/clo­jurewest2012/#/title-slide
  2. Seesaw na GitHubu
    https://github.com/daveray/seesaw
  3. Seesaw API Documentation
    http://daveray.github.io/seesaw/
  4. Seesaw wiki
    https://github.com/davera­y/seesaw/wiki
  5. seesaw-repl-tutorial.clj
    https://gist.github.com/da­veray/1441520
  6. Témata o Seesaw na Google groups
    https://groups.google.com/fo­rum/#!forum/seesaw-clj
  7. Threading macro (dokumentace k jazyku Clojure)
    https://clojure.github.io/clo­jure/clojure.core-api.html#clojure.core/->
  8. Understanding the Clojure → macro
    http://blog.fogus.me/2009/09/04/un­derstanding-the-clojure-macro/
  9. Clisk
    https://github.com/mikera/clisk
  10. clojars: net.mikera/clisk
    https://clojars.org/net.mikera/clisk
  11. clojure.inspector
    http://clojure.github.io/clo­jure/clojure.inspector-api.html
  12. Clisk: wiki
    https://github.com/mikera/clisk/wiki
  13. Dokumentace vygenerovaná pro knihovnu core.matrix
    https://cloojure.github.i­o/doc/core.matrix/index.html
  14. Size and Dimensionality
    https://groups.google.com/fo­rum/#!topic/numerical-clojure/zebBCa68eTw/discussion
  15. Towards core.matrix for Clojure?
    https://clojurefun.wordpres­s.com/2013/01/05/towards-core-matrix-for-clojure/
  16. The Clojure Toolbox
    http://www.clojure-toolbox.com/
  17. Neanderthal
    http://neanderthal.uncomplicate.org/
  18. Hello world project
    https://github.com/uncompli­cate/neanderthal/blob/mas­ter/examples/hello-world/project.clj
  19. vectorz-clj
    https://github.com/mikera/vectorz-clj
  20. vectorz – Examples
    https://github.com/mikera/vectorz-clj/wiki/Examples
  21. gloss
    https://github.com/ztellman/gloss
  22. HTTP client/server for Clojure
    http://www.http-kit.org/
  23. Array Programming
    https://en.wikipedia.org/wi­ki/Array_programming
  24. Discovering Array Languages
    http://archive.vector.org­.uk/art10008110
  25. no stinking loops – Kalothi
    http://www.nsl.com/
  26. Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
    http://www.vector.org.uk/
  27. APL Interpreters
    http://www.vector.org.uk/?a­rea=interpreters
  28. APL_(programming_language
    http://en.wikipedia.org/wi­ki/APL_(programming_langu­age
  29. APL FAQ
    http://www.faqs.org/faqs/apl-faq/
  30. APL FAQ (nejnovější verze)
    http://home.earthlink.net/~swsir­lin/apl.faq.html
  31. A+
    http://www.aplusdev.org/
  32. APLX
    http://www.microapl.co.uk/
  33. FreeAPL
    http://www.pyr.fi/apl/index.htm
  34. J: a modern, high-level, general-purpose, high-performance programming language
    http://www.jsoftware.com/
  35. K, Kdb: an APL derivative for Solaris, Linux, Windows
    http://www.kx.com
  36. openAPL (GPL)
    http://sourceforge.net/pro­jects/openapl
  37. Parrot APL (GPL)
    http://www.parrotcode.org/
  38. Learning J (Roger Stokes)
    http://www.jsoftware.com/hel­p/learning/contents.htm
  39. Rosetta Code
    http://rosettacode.org/wiki/Main_Page
  40. Why APL
    http://www.acm.org/sigapl/whyapl.htm
  41. java.jdbc API Reference
    https://clojure.github.io/java.jdbc/
  42. Hiccup
    https://github.com/weavejester/hiccup
  43. Clojure Ring na GitHubu
    https://github.com/ring-clojure/ring
  44. A brief overview of the Clojure web stack
    https://brehaut.net/blog/2011/rin­g_introduction
  45. Getting Started with Ring
    http://www.learningclojure­.com/2013/01/getting-started-with-ring.html
  46. Getting Started with Ring and Compojure – Clojure Web Programming
    http://www.myclojureadven­ture.com/2011/03/getting-started-with-ring-and-compojure.html
  47. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  48. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  49. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  50. Leiningen: úvodní stránka
    http://leiningen.org/
  51. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  52. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  53. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  54. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  55. 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/
  56. 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/
  57. 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/
  58. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  59. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  60. 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/
  61. 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/
  62. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  63. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  64. 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/
  65. 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/
  66. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  67. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  68. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  69. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  70. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  71. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  72. 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/
  73. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
Našli jste v článku chybu?
Měšec.cz: Za palivo zaplatíte mobilem (TEST)

Za palivo zaplatíte mobilem (TEST)

DigiZone.cz: R2B2 a Hybrid uzavřely partnerství

R2B2 a Hybrid uzavřely partnerství

Vitalia.cz: 7 originálních adventních kalendářů pro mlsné

7 originálních adventních kalendářů pro mlsné

DigiZone.cz: Sat novinky: slovenská TV8 HD i ruský NTV Mir

Sat novinky: slovenská TV8 HD i ruský NTV Mir

DigiZone.cz: V Plzni odstartovalo Radio 1

V Plzni odstartovalo Radio 1

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Podnikatel.cz: E-Ježíšek si zařádí: nákupy od 2 do 5 tisíc

E-Ježíšek si zařádí: nákupy od 2 do 5 tisíc

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Vitalia.cz: Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Vitalia.cz: Jak vybrat ořechy do cukroví a kde mají levné

Jak vybrat ořechy do cukroví a kde mají levné

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Vitalia.cz: Nejlepší obranou při nachlazení je útok

Nejlepší obranou při nachlazení je útok

DigiZone.cz: Česká televize mění schéma ČT :D

Česká televize mění schéma ČT :D

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?