V pondělí 24. října zemřel ve věku 84 let vynálezce Lispu John McCarthy. Je zajímavé, že McCarthy Lisp pouze navrhl - v roce 1958 publikoval článek Recursive Functions of Symbolic Expressions and Their Computation by Machine. Jazyk samotný byl později implementován Stevem Russellem.
Zajímavé také je, že McCarthy původně používal M-expressions (meta expressions) v hranatých závorkách, které se ovšem dají přeložit na klasické "lispovské" S-expressions (symbolic expressions). Když byl ale Lisp implementován, začali programátoři používat právě s-expressions a tak McCarthy od používání m-expressions upustil.
McCarthy se do historie nezapsal pouze jako tvůrce Lispu, ale i mnoha dalšími počiny, např. vynálezem garbage collection (mmj. právě kvůli problémům ohledně Lispu), nebo zavedením termínu Artificial Intelligence.
27. října 2011
16. srpna 2011
ThoughtWorks Radar zmiňuje Clojure
Firmu ThoughtWorks znám hlavně protože zde pracuje můj oblíbený SW guru Martin Fowler. Nedávno vyšel jejich Technology Radar, který zmiňuje "rozvíjející se technologie a trendy, které ovlivňují dnešní trh".
Pro tento blog je podstatné, že je zde třikrát zmíněno Clojure. Jednak jako samotný jazyk:
"Clojure is a dynamic, functional language that runs on the JVM. Although its roots are in Lisp, one of the oldest computer languages, it also embodies many modern programming concepts, including lazy evaluation and advanced concurrency abstractions. Clojure has spawned a vibrant community of programmers who are contributing a rich set of frameworks and tools. One example of these is Midje, an innovative spin on unit testing and mocking frameworks."
Clojure se v sekci Languages nachází v kruhu Assess, to znamená "Worth exploring with the goal of understanding how it will affect your enterprise.".
Dále je v sekci Interesting Tools uveden framework Midje, "nástroj pro čitelné testy v Clojure". A konečně je Clojure zmíněno v souvislosti s cloud platformou Heroku, kam bylo přidáno jako jeden z jazyků.
Z trochu jiného úhlu pohledu píšu o Radaru, na svém blogu o softwarovém inženýrství, SoftWare Samuraj.
Pro tento blog je podstatné, že je zde třikrát zmíněno Clojure. Jednak jako samotný jazyk:
"Clojure is a dynamic, functional language that runs on the JVM. Although its roots are in Lisp, one of the oldest computer languages, it also embodies many modern programming concepts, including lazy evaluation and advanced concurrency abstractions. Clojure has spawned a vibrant community of programmers who are contributing a rich set of frameworks and tools. One example of these is Midje, an innovative spin on unit testing and mocking frameworks."
Clojure se v sekci Languages nachází v kruhu Assess, to znamená "Worth exploring with the goal of understanding how it will affect your enterprise.".
Dále je v sekci Interesting Tools uveden framework Midje, "nástroj pro čitelné testy v Clojure". A konečně je Clojure zmíněno v souvislosti s cloud platformou Heroku, kam bylo přidáno jako jeden z jazyků.
Z trochu jiného úhlu pohledu píšu o Radaru, na svém blogu o softwarovém inženýrství, SoftWare Samuraj.
15. srpna 2011
Testování v Clojure
Ačkoliv Rich Hickey říká, že nepíše unit testy, přece jenom do Clojure zahrnul "framework" pro unit testy. Já sám, naopak, jsem již léty TDD infikován a testy píšu rád. Takže, jak se to dělá v Clojure? Nejprve natáhneme knihovnu clojure.test:
Test first! Napíšeme testy (soubor core.clj v adresáři test):
(ns my-tests (:use clojure.test)) ; nil
Aserce (tvrzení)
Základem testů je makro is:(is (= 2 (+ 1 1))) ; true (is (odd? 1)) ; true (is (even? 1) "truth about one") ; ; FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:30) ; truth about one ; expected: (even? 1) ; actual: (not (even? 1)) ; falseVíce assertions/parametrů se dá testovat pomocí makra are:
(are [x y] (= 42 (+ x y)) 21 21 20 22 42 0) ; trueVyhození výjimky se dá otestovat pomocí funkce thrown?:
(is (thrown? ArithmeticException (/ 42 0))) ; #<ArithmeticException java.lang.ArithmeticException: Divide by zero> (is (thrown? ArithmeticException (/ 42 42))) ; ; FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:48) ; expected: (thrown? ArithmeticException (/ 42 42)) ; actual: nil ; nil
Definice testů
OK, to byly tvrzení (assertions). Jak definuji test? Jedna z možností je makro with-test:(with-test (defn add [x y] (+ x y)) (is (= 42 (add 21 21))) (is (= 42 (add 20 22))) (is (= 42 (add 42 0))))Druhou možností je použít makro deftest. Jeho výhodou je, že vytváří funkci (v našem případě ultimate-addition), kterou lze zavolat z libovolného namespace. To umožňuje mít testy v samostatném souboru tak, jak jsme z unit testů zvyklí.
(deftest ultimate-addition (is (= 42 (add 21 21))) (is (= 42 (add 20 22))) (is (= 42 (add 42 0))))
Spuštění testů
Máme definované dva testy (a šest asercí). Jak je spustíme? Testy v aktuálním namespace odpálíme funkcí run-tests:(run-tests) ; ; Testing my-tests ; ; Ran 2 tests containing 6 assertions. ; 0 failures, 0 errors. ; {:type :summary, :pass 6, :test 2, :error 0, :fail 0}Testy v jiném namespace spustíme obdobně:
(run-tests 'some.namespace 'some.other.namespace)
Automatizace testů
Tak. Konec srandiček! Velký kluci buildujou (a spouští testy) Leiningenem. Prvně vytvoříme projekt příkazem lein new lein-tests:Vytvoření a layout projektu |
Test first! Napíšeme testy (soubor core.clj v adresáři test):
(ns lein-tests.test.core (:use [lein-tests.core]) (:use [clojure.test])) (deftest split-line-test (is (= ["foo" "bar"] (split-line "foo;bar" #";"))) (is (= ["foo" "bar"] (split-line "foo,bar" #", ?"))) (is (= ["foo" "bar"] (split-line "foo, bar" #", ?")))) (deftest get-phone-test (is (= "123456789" (get-phone "123456789;Guido"))) (is (= "" (get-phone ";Guido"))))A napíšeme (testované) funkce (soubor core.clj v adresáři src):
(ns lein-tests.core (:use [clojure.string :only (split)])) (defn split-line [line separator] (split line separator)) (defn get-phone [line] (first (split-line line #";")))No a na závěr testy spustíme příkazem lein test. Nádhera!
Spuštění testů |
13. srpna 2011
Změna domény: clojure.cz
Tak je to už sedm měsíců, co píšu Sometimes Clojure. Včera jsem se jen tak z rozmaru podíval, jestli je volná doména clojure.cz. Byla. Tak jsem si ji vzal. Zatím se mi zdá, že jsem jediný, kdo o Clojure v Česku píše, tak co. Třeba jednou vznikne komunitní web, nebo něco. Ale teď je moje! My precious!! :-)
11. srpna 2011
Nazdar světe! říká ClojureScript
Tak jsem se neudržel a ClojureScript si vyzkoušel. Následuje popis (jednoho zápasu), jak si vyrobit Hello, world! v ClojureScriptu. Vzhledem k tomu, že ClojureScript je zatím v alpha verzi a Windows (kde jsem to musel z časových důvodů vyzkoušet) tradičně hází vývojářům klacky pod nohy :-/ se instalace, kompilace a vyzkoušení triviálního příkladu protáhlo na několik hodin. Nicméně zvítězil jsem a tady je výsledek.
Nebudu se zabývat instalací ClojureScriptu - je dobře popsána ve wiki projektu, Windows instalace je trochu dramatičtější. Navíc v případě Windows je problém, pokud se kompilace provádí v cestě s mezerami (Documents and Settings apod.), je při optimalizovaném překladu vyhozena výjimka.
Tak, máme nainstalováno? Spustíme z příkazové řádky příkaz cljsc (ClojureScript compiler):
C:\clojure-clojurescript\samples\my-hello>cljsc Usage: "cljsc <file-or-dir> > out.js" "cljsc <file-or-dir> {:optimiztions :advanced} > out.js"
No. Co/k čemu ten ClojureScript vlastně je? ClojureScript je dialekt Clojure (spíš podmnožina). Nejdůležitější je jeho kompilátor, který umožňuje zkompilovat Clojure(Script) do JavaScriptu. Pokud by někoho zajímaly filozofické důvody pro vznik ClojureScriptu, jsou uvedeny ve wiki na stránce Rationale (odůvodnění). V podstatě jde asi o to, dostat Clojure i na jinou platformu než JVM, v tomto případě JavaScript VM.
ClojureScript kompilátor generuje JavaScript, který (záměrně) odpovídá konvenci JavaScriptové knihovny Google Closure. Důvod je zřejmý - (Google) Closure Compiler. Tím se vysvětlují dva způsoby použití kompilátoru cljsc:
Uf! Byla to dřina. :-|
Když to vezmu prakticky, tak zatím ClojureScript moc použitelný není. Problém jsou hlavně chybějící nástroje. Pokud ale jednou bude zaintegrován například do Leiningenu nebo Mavenu, tak si dokážu představit, že třeba ve spojení s Compojure půjde vytvořit kompletní webovou (klient/server) aplikaci pouze v Clojure. To by bylo komfortní.
ClojureScript kompilátor generuje JavaScript, který (záměrně) odpovídá konvenci JavaScriptové knihovny Google Closure. Důvod je zřejmý - (Google) Closure Compiler. Tím se vysvětlují dva způsoby použití kompilátoru cljsc:
- cljsc <file-or-dir> > out.js spustí pouze ClojureScript kompilátor a výsledný soubor "slinkuje" s knihovnou Closure Library.
- cljsc <file-or-dir> {:optimiztions :advanced} > out.js dělá totéž, plus ještě následně prožene výsledný kód Closure Compilerem, který kód zoptimalizuje a vytvoří jeden kompaktní soubor.
(ns hello) (defn ^:export greet [n] (str "Hello, " n "!"))Následně ho zkompiluju:
cljsc hello.cljs {:optimizations :advanced} > hello.jsA výsledný kód zavolám v prohlížeči:
<html> <head> <title>ClojureScript</title> <script src="hello.js" type="text/javascript"> </script> </head> <body> <script type="text/javascript"> alert(hello.greet("ClojureScript")); </script> </body> </html>A výsledek?
Uf! Byla to dřina. :-|
Když to vezmu prakticky, tak zatím ClojureScript moc použitelný není. Problém jsou hlavně chybějící nástroje. Pokud ale jednou bude zaintegrován například do Leiningenu nebo Mavenu, tak si dokážu představit, že třeba ve spojení s Compojure půjde vytvořit kompletní webovou (klient/server) aplikaci pouze v Clojure. To by bylo komfortní.
10. srpna 2011
PragPub píše o ClojureScriptu
V uplynulých dnech se mojí (Google Reader) čtečkou prohnala smršť zpráviček a anoncí o ClojureScriptu. Nevím, jestli to není jen hype, který se kumuluje kolem CoffeeScriptu. Navíc ClojureScript je již nějaký čas součástí knihovny clojure-contrib.
Každopádně aktuální číslo mého oblíbeného e-časopisu PragPub obsahuje článek o ClojureScriptu. Kromě HTML jsou k dispozici i formáty mobi, epub a PDF.
Každopádně aktuální číslo mého oblíbeného e-časopisu PragPub obsahuje článek o ClojureScriptu. Kromě HTML jsou k dispozici i formáty mobi, epub a PDF.
29. července 2011
Jak měnit neměnitelné. Refs
Clojure používá neměnitelná (immutable) data/objekty. Pokud potřebujeme měnitelná (mutable) data, řeší to Clojure "měnitelnou referencí na neměnitelný objekt" :-) Jedním z prostředků, které toto řeší jsou Refs: jsou to transakční reference, které umožňují bezpečné sdílení měnitelných úložišť pomocí systému STM (Software Transactional Memory).
Měnitelná reference (ref) se vytvoří funkcí ref, její hodnotu vrací buď funkce deref, nebo makro @:
Při rozhodování, jestli použít ref nebo atom je podstatné, jestli využiju transakci - v transakci můžu updatovat více referencí. Pokud budu měnit pouze jedinou hodnotu (bez vazby na cokoli jiného) je vhodnější použít atom (a o těch až někdy příště).
Měnitelná reference (ref) se vytvoří funkcí ref, její hodnotu vrací buď funkce deref, nebo makro @:
(def my-ref (ref "immutable data")) ; #'user/my-ref my-ref ; #<ref@18352d8: "immutable data"> @my-ref ; "immutable data" (deref my-ref) ; "immutable data"Pokud chceme referenci nastavit na jinou (neměnitelnou) hodnotu, slouží k tomu funkce ref-set:
(ref-set my-ref "another immutable data") ; IllegalStateException No transaction running clojure.lang.LockingTransaction.getEx (LockingTransaction.java:208)Jejda! Zapomněli jsme na transakci :-)
(dosync (ref-set my-ref "another immutable data")) ; "another immutable data" @my-ref ; "another immutable data"Pokud chceme provést čtení hodnoty a zároveň její změnu v jednom kroku (= aplikovat na hodnotu funkci), je vhodné použít funkci alter:
(dosync (alter my-ref #(apply str (reverse %)))) ; "atad elbatummi rehtona" @my-ref ; "atad elbatummi rehtona"Na reference je také možné přidat validace:
(def counter (ref 0 :validator number?)) ; #'user/counter (dosync (ref-set counter "string")) ; IllegalStateException Invalid reference state clojure.lang.ARef.validate (ARef.java:33) @counter ; 0 (dosync (ref-set counter 42)) ; 42 @counter ; 42O Refs jsem už jednou (trochu) psal. Z dnešního pohledu k tomu mám dvě výhrady:
- místo ref-set jsem měl použít alter,
- místo reference jsem měl použít atom.
Při rozhodování, jestli použít ref nebo atom je podstatné, jestli využiju transakci - v transakci můžu updatovat více referencí. Pokud budu měnit pouze jedinou hodnotu (bez vazby na cokoli jiného) je vhodnější použít atom (a o těch až někdy příště).
24. července 2011
Nekonečná lenost sekvencí
Velmi silnou (a zajímavou) zbraní Clojure jsou sekvence (neboli seq, čti [si:k]). Sekvence je logický seznam, který implementuje jednoduché rozhraní ISeq a umožňuje sekvenční přístup k datům - a to nejenom k těm, u kterých bychom to čekali (kolekce = seznamy, mapy, apod.), ale i k těm, kde potřebné sekvenční implementační detaily chybí, např. stromové struktury (XML, adresáře), databázové result sety či textové soubory (buď jeden velký řetězec, nebo vektor řádků), I/O streamy, anebo obyčejné znakové řetězce (String).
Věc, na kterou bych se chtěl podívat je jednak lenost (laziness) a jednak (možná) nekonečnost (infiniteness) sekvencí. Laziness je známá např. z databázového/ORM světa, kdy se lazy typicky dotahují data v 1:N vztazích. Výhodou lazy sekvencí je:
Co se týká nekonečnosti, tam je to jasné - některé sekvence prostě jsou nekonečné: přirozená čísla, prvočísla, Fibonacciho posloupnost, atd.
Ukázkový příklad jsem převzal z knížky Programming Clojure a sice protože mi přišel tak dobrý, že se mi zdálo zbytečné vymýšlet příklad vlastní (ach ta lenost :-). Zajímá vás, jak vypadá milionté prvočíslo?
Pro vysvětlení, Var primes obsahuje lazy sekvenci prvočísel, Var ordinals-and-primes obsahuje dvojice hodnot [pořadové-číslo prvočíslo]. Poslední příkaz (first (drop ... provede samotný výpočet sekvence prvočísel, zahodí prvních 999.999 hodnot a vrátí (pomocí first) tu 1.000.000tou. Vypočítat milion prvočísel chvilku trvá, takže třetí příkaz chvilku poběží. Spočítanou sekvenci už má pak ale Clojure nacachovanou, takže hodnoty pod milion nebo lehce nad vrací okamžitě.
Věc, na kterou bych se chtěl podívat je jednak lenost (laziness) a jednak (možná) nekonečnost (infiniteness) sekvencí. Laziness je známá např. z databázového/ORM světa, kdy se lazy typicky dotahují data v 1:N vztazích. Výhodou lazy sekvencí je:
- odsunutí výpočtu, který možná nebude potřeba,
- možnost práce s velkými daty, která se nevejdou do paměti,
- odsunutí I/O operací, až budou opravdu potřeba.
Co se týká nekonečnosti, tam je to jasné - některé sekvence prostě jsou nekonečné: přirozená čísla, prvočísla, Fibonacciho posloupnost, atd.
Ukázkový příklad jsem převzal z knížky Programming Clojure a sice protože mi přišel tak dobrý, že se mi zdálo zbytečné vymýšlet příklad vlastní (ach ta lenost :-). Zajímá vás, jak vypadá milionté prvočíslo?
(use '[clojure.contrib.lazy-seqs :only (primes)]) ; nil (def ordinals-and-primes (map vector (iterate inc 1) primes)) ; #'user/ordinals-and-primes (first (drop 999999 ordinals-and-primes)) ; [1000000 15485863]
Pro vysvětlení, Var primes obsahuje lazy sekvenci prvočísel, Var ordinals-and-primes obsahuje dvojice hodnot [pořadové-číslo prvočíslo]. Poslední příkaz (first (drop ... provede samotný výpočet sekvence prvočísel, zahodí prvních 999.999 hodnot a vrátí (pomocí first) tu 1.000.000tou. Vypočítat milion prvočísel chvilku trvá, takže třetí příkaz chvilku poběží. Spočítanou sekvenci už má pak ale Clojure nacachovanou, takže hodnoty pod milion nebo lehce nad vrací okamžitě.
17. července 2011
PragPub se věnuje Clojure
Vydavatelství The Pragmatic Bookshelf, které svého času založili někdejší signatáři Manifestu agilního vývoje softwaru Andy Hunt a Dave Thomas, vydává (zdarma) příjemný časopis, měsíčník PragPub. Aktuální červencové číslo se věnuje převážně Clojure. Jsou zde mmj. články o neměnnosti dat a kolekcích, nebo o DSL.
Kromě odkazovaného HTML vydání jsou na stránce vydaných čísel magazínu k dispozici i formáty mobi, epub a PDF.
Kromě odkazovaného HTML vydání jsou na stránce vydaných čísel magazínu k dispozici i formáty mobi, epub a PDF.
15. června 2011
Rich Hickey: Radši trávím čas přemýšlením o problému
Skončil mi v Readeru zajímavý rozhovor s Richem Hickeym. Zajímavý byl i dotazovatel - Michael Fogus, autor knihy The Joy of Clojure. Rozhovor je trochu delší a dost špatně (typograficky) formátovaný, ale kvůli tomu obsahu stojí za to se tím prokousat.
Rich hovoří o svých zkušenostech programátora, co bylo před Clojure, co Clojure pozitivně a negativně ovlivnilo, apod. Zmíněn je také Richův seznam na Amazonu Clojure Bookshelf (mmch. inspirativní seznam pro čtení, už před časem jsem si tam pár kousků vybral).
Mmj. mě zaujalo, že Rich je spíš na debuggování (debugging master) a nepíše testy (TDD):
"If people think that spending fifty percent of their time writing tests maximizes their results—okay for them. I’m sure that’s not true for me—I’d rather spend that time thinking about my problem. I’m certain that, for me, this produces better solutions, with fewer defects, than any other use of my time."
A co dělá Rich pro zlepšení svých programátorských skillů?
"I read relentlessly. I don’t do any programming not directed at making the computer do something useful, so I don’t do any exercises. I try to spend more time thinking about the problem than I do typing it in."
Rich hovoří o svých zkušenostech programátora, co bylo před Clojure, co Clojure pozitivně a negativně ovlivnilo, apod. Zmíněn je také Richův seznam na Amazonu Clojure Bookshelf (mmch. inspirativní seznam pro čtení, už před časem jsem si tam pár kousků vybral).
Mmj. mě zaujalo, že Rich je spíš na debuggování (debugging master) a nepíše testy (TDD):
"If people think that spending fifty percent of their time writing tests maximizes their results—okay for them. I’m sure that’s not true for me—I’d rather spend that time thinking about my problem. I’m certain that, for me, this produces better solutions, with fewer defects, than any other use of my time."
A co dělá Rich pro zlepšení svých programátorských skillů?
"I read relentlessly. I don’t do any programming not directed at making the computer do something useful, so I don’t do any exercises. I try to spend more time thinking about the problem than I do typing it in."
27. dubna 2011
Second mission
Navázal bych na post First Mission - napsal jsem další transformační skriptík pro stejný projekt. Opět šlo o to, přečíst vstupní soubor a na základě daných podmínek změnit některé hodnoty. Soubor obsahoval informaci o sdílených telefonech:
Tam, kde byla pro stejný telefon různá osobní čísla mělo dojít ke změně typu z PERSONAL na SHARED:
Měl jsem trochu obavy z "performance" (soubory mají 10k - 50k řádků), tak jsem to udělal na dva průchody - při prvním průchodu se do mapy vložily telefony, které měly více os. čísel a při druhém průchodu, kdy se všechny záznamy zapisovaly do upraveného souboru, se tyto záznamy měnily.
Skriptík je samozřejmě triviální, ale podstatné jsou věci, které jsem se přitom naučil - pořádně pracovat s Refs a transakcema (STM):
Tam, kde byla pro stejný telefon různá osobní čísla mělo dojít ke změně typu z PERSONAL na SHARED:
Měl jsem trochu obavy z "performance" (soubory mají 10k - 50k řádků), tak jsem to udělal na dva průchody - při prvním průchodu se do mapy vložily telefony, které měly více os. čísel a při druhém průchodu, kdy se všechny záznamy zapisovaly do upraveného souboru, se tyto záznamy měnily.
Skriptík je samozřejmě triviální, ale podstatné jsou věci, které jsem se přitom naučil - pořádně pracovat s Refs a transakcema (STM):
(def phones (ref {})) (defn add-phone [phone pers-num] (if (contains? @phones phone) (if (contains? (@phones phone) pers-num) (println "Duplicate record:" phone pers-num) (dosync (ref-set phones (update-in @phones [phone] conj pers-num)))) (dosync (ref-set phones (assoc @phones phone #{pers-num}))))) (add-phone :123456789 :12) (add-phone :123456789 :42) (add-phone :123456789 :12) (add-phone :123456790 :36) (println @phones) ; Duplicate record: :123456789 :12 ; {:123456790 #{:36}, :123456789 #{:12 :42}}Další šikovná věc je "filrování mapy":
(defn get-shared [m] (select-keys m (for [[k v] m :when (> (count v) 1)] k))) (println (get-shared {:123456790 #{:36}, :123456789 #{:12 :42}}) ; {:123456789 #{:12 :42}}K tématu Refs bych se v budoucnu ještě rád vrátil - hned potom, co ze zásobníku vyndám a zpracuju sekvence.
12. února 2011
Uzávěr v laboratoři (jak začít)
Dneska bych tady měl dvě "drobnosti" pro ty, kdo chtějí s Clojure začít. Prvně to kratší: Chcete zkusit Clojure a přitom nic neinstalovat, jen si vyzkoušet jazyk? Na stránce Try Clojure je k dispozici online REPL, včetně krátkého tutoriálu. Jako perlička - k dispozici je Google Chrome rozšíření try-clojure-in-chrome, které zmíněnou stránku využívá pro zobrazení REPLu v Chrome.
[Updated: Zdá se, že posledních pár dní je stránka Try Clojure mrtvá. :-/ ]
Hlavní věc, o které bych ale dnes chtěl psát, je výborný Clojure tutoriál labrepl. Tutoriál vytvořil Stuart Halloway, autor vůbec první knihy o Clojure Programming Clojure a také jeden z hlavních contributorů do Clojure samotného.
Musím říct, že labrepl mě vyloženě nadchnul a to jak obsahem, tak implementací. Celý tutoriál je totiž v Clojure jak napsaný (HTML je generovaný pomocí Clojure web frameworku Compojure), tak zbuildovaný (konrétně Leiningenem, o kterém bych chtěl něco napsat příště). Tutoriál se spustí jako REPL, přičemž v rámci něho na pozadí běží web server Jetty s HTML tutoriálem a v již spuštěném REPLu se dají zkoušet jednotlivé příklady. Tento neohrabaný popis asi není moc názorný, takže radši doporučuji přímo vyzkoušet.
Nebudu tady popisovat obsah tutoriálu, jen bych zmínil věc, na kterou jsem zatím online nenarazil. A to je dokumentace přímo v REPLu. To je další vlastnost, která mne nadchla - zatím jsem se nesetkal s jazykem (s částečnou výjimkou v Pythonu), který by tak snadno a v takovém rozsahu něco podobného poskytoval.
Dokumentace funkce:
Vyhledání dokumentace:
Funkce javadoc otevře stránku Javadocu v prohlížeči (buď lokální, nebo vzdálenou):
A nakonec funkce, která je nejvíc cool! source zobrazí implementaci dané funkce:
Hlavní věc, o které bych ale dnes chtěl psát, je výborný Clojure tutoriál labrepl. Tutoriál vytvořil Stuart Halloway, autor vůbec první knihy o Clojure Programming Clojure a také jeden z hlavních contributorů do Clojure samotného.
Musím říct, že labrepl mě vyloženě nadchnul a to jak obsahem, tak implementací. Celý tutoriál je totiž v Clojure jak napsaný (HTML je generovaný pomocí Clojure web frameworku Compojure), tak zbuildovaný (konrétně Leiningenem, o kterém bych chtěl něco napsat příště). Tutoriál se spustí jako REPL, přičemž v rámci něho na pozadí běží web server Jetty s HTML tutoriálem a v již spuštěném REPLu se dají zkoušet jednotlivé příklady. Tento neohrabaný popis asi není moc názorný, takže radši doporučuji přímo vyzkoušet.
Nebudu tady popisovat obsah tutoriálu, jen bych zmínil věc, na kterou jsem zatím online nenarazil. A to je dokumentace přímo v REPLu. To je další vlastnost, která mne nadchla - zatím jsem se nesetkal s jazykem (s částečnou výjimkou v Pythonu), který by tak snadno a v takovém rozsahu něco podobného poskytoval.
Dokumentace funkce:
(doc zipmap) ; ------------------------- ; clojure.core/zipmap ; ([keys vals]) ; Returns a map with the keys mapped to the corresponding vals.
Vyhledání dokumentace:
(find-doc "zip") ; ------------------------- ; clojure.zip/seq-zip ; ([root]) ; Returns a zipper for nested sequences, given a root sequence ; ------------------------- ; clojure.zip/vector-zip ; ([root]) ; Returns a zipper for nested vectors, given a root vector ; ------------------------- ; clojure.zip/xml-zip ; ([root]) ; Returns a zipper for xml elements (as from xml/parse), ; given a root element
Funkce javadoc otevře stránku Javadocu v prohlížeči (buď lokální, nebo vzdálenou):
(javadoc java.util.Date) ; "http://java.sun.com/javase/6/docs/api/java/util/Date.html" ; Created new window in existing browser session.
A nakonec funkce, která je nejvíc cool! source zobrazí implementaci dané funkce:
(source drop) ; (defn drop ; "Returns a lazy sequence of all but the first n items in coll." ; {:added "1.0"} ; [n coll] ; (let [step (fn [n coll] ; (let [s (seq coll)] ; (if (and (pos? n) s) ; (recur (dec n) (rest s)) ; s)))] ; (lazy-seq (step n coll))))
9. února 2011
Sní androidi o Clojure?
Jsem asi poslední javista, který ještě nemá telefon s Androidem. Samozřejmě, že si ho jednou pořídím - je mi zkrátka sympatičtější než iOS nebo Symbian. Billův paskvil radši ani nebudu zmiňovat jménem :-) Každopádně mě napadlo, jak je na tom Android s Clojure (nebo naopak?).
Dobrá zpráva je, že Clojure Core počítá s podporou Androidu. Doslova je uvedeno "Clojure should run well there.". Horší je, že start aplikace zatím není příliš svižný (sluggish). Taky velikost aplikace je řádově rozdílná: Java 20 KB, Clojure 4 MB :-(
Nicméně jsem optimista - až si toho Androida jednou koupím, Clojure na něm bude svištět jako vítr :-)
Dobrá zpráva je, že Clojure Core počítá s podporou Androidu. Doslova je uvedeno "Clojure should run well there.". Horší je, že start aplikace zatím není příliš svižný (sluggish). Taky velikost aplikace je řádově rozdílná: Java 20 KB, Clojure 4 MB :-(
Nicméně jsem optimista - až si toho Androida jednou koupím, Clojure na něm bude svištět jako vítr :-)
3. února 2011
Currying
Čtu teď knížku Groovy for Domain-Specific Languages a zaujala mě tam technika, kterou jsem doposud neznal - currying. V krátkosti jde o transformaci funkce, která má více argumentů (nebo pole argumentů) tak, že může být zavolána jako řetěz funkcí s jedním argumentem. Zapsáno matematicky: g(y) = (y -> f(x,y)). Funkce g je "(z)kariovaná" (curried) verze funkce f.
Pojem currying je spojován hlavně se jménem matematika Haskella Curryho. Ano, je to ten Haskell, po kterém je pojmenován (čistě) funkcionální jazyk Haskell. Currying je vlastní v podstatě všem funkcionálním jazykům, za zmínku stojí speciálně Haskell a ML, kde všechny funkce mají přesně jeden argument, tj. jsou "kariované". "Kariované" funkce by také měly jít použít/vytvořit ve všech jazycích, které podporují closures, namátkou:
Pojem currying je spojován hlavně se jménem matematika Haskella Curryho. Ano, je to ten Haskell, po kterém je pojmenován (čistě) funkcionální jazyk Haskell. Currying je vlastní v podstatě všem funkcionálním jazykům, za zmínku stojí speciálně Haskell a ML, kde všechny funkce mají přesně jeden argument, tj. jsou "kariované". "Kariované" funkce by také měly jít použít/vytvořit ve všech jazycích, které podporují closures, namátkou:
No a tenhle blog je o Clojure, takže jak to vypadá v Clojure?
V Clojure není potřeba syslit argumenty po jednom, ale dá se jich zadat víc naráz (1-n):
Závěrem, k čemu je vlastně currying dobrý? Kromě toho, že může zpřehlednit/zjednodušit funkce s mnoha argumenty (lambda kalkul v matematice), je hlavní výhoda použití v doplňování argumentů on-the-fly - doplním argumenty, které aktuálně znám a ostatní doplním, až budou k dispozici. Vzhledem k tomu, že Clojure je lazy-init, může být currying konvenující metoda.
2. února 2011
First mission
Tak mám za sebou první prográmek v Clojure. Kolega potřeboval "přestrukturovat" data v CSV souboru a tak jsem si řekl, že je to vhodná příležitost vyzkoušet si Clojure v akci. V principu šlo o následující problém: zdrojová data vypadala nějak takto:
Největší problém na který jsem narazil, byla produktivita. Zatímco v Groovy bych měl úlohu vyřešenou cca za dvě hodiny, v Clojure mi to trvalo dva dny. Na druhé místo bych umístil I/O operace - postupy uvedené na webu nejsou aktuální vůči stávající verzi Clojure 1.2, takže malá ukázka:
Makro with-open je použito, aby se otevřené streamy na konci zavřely (automaticky zavolá .close), instance readeru a writeru jsou BufferedReader a BufferedWriter.
A za třetí bych uvedl svoji neznalost "funkcionálního způsobu myšlení". Přece jenom, byl to boj, pracovat s neměnitelnými datovými strukturami. Vyzkoušel jsem si při tom trochu práci s Vars.
Vars poskytují mechanismus jak odkazovat na úložiště, které se může dynamicky měnit. Pomocí formu def vytvořím (globální) Var objekt, který inicializuju nějakou hodnotou. Pokud chci, aby vytvořený objekt dočasně odkazoval na jinou hodnotu, můžu ho, v rámci jednoho threadu, přesměrovat na úložiště s jinou hodnotou pomocí makra binding:
Cílova data takto:
Největší problém na který jsem narazil, byla produktivita. Zatímco v Groovy bych měl úlohu vyřešenou cca za dvě hodiny, v Clojure mi to trvalo dva dny. Na druhé místo bych umístil I/O operace - postupy uvedené na webu nejsou aktuální vůči stávající verzi Clojure 1.2, takže malá ukázka:
Makro with-open je použito, aby se otevřené streamy na konci zavřely (automaticky zavolá .close), instance readeru a writeru jsou BufferedReader a BufferedWriter.
; read file (with-open [reader (clojure.java.io/reader "source.csv" :encoding "Cp1250")] (println (line-seq reader))) ; write file (with-open [writer (clojure.java.io/writer "target.csv" :encoding "ISO8859_2")] (.write writer "string"))
A za třetí bych uvedl svoji neznalost "funkcionálního způsobu myšlení". Přece jenom, byl to boj, pracovat s neměnitelnými datovými strukturami. Vyzkoušel jsem si při tom trochu práci s Vars.
Vars poskytují mechanismus jak odkazovat na úložiště, které se může dynamicky měnit. Pomocí formu def vytvořím (globální) Var objekt, který inicializuju nějakou hodnotou. Pokud chci, aby vytvořený objekt dočasně odkazoval na jinou hodnotu, můžu ho, v rámci jednoho threadu, přesměrovat na úložiště s jinou hodnotou pomocí makra binding:
(def x 42) ; #'user/x x ; 42 (binding [x 12] x) ; 12 x ; 42
27. ledna 2011
Functions without side effects
Pročítal jsem si na webu nějaké materiály o funkcionálním programování. Jedním ze základních principů je, že funkce nesmí mít vedlejší efekty (side effects). Výčet a definice těchto principů se v různých zdrojích liší, mne konkrétně oslovily tyto:
- Pokud je funkce volána s parametry, které nezpůsobují vedlejší efekty, její opakované volání (se stejnými parametry) vrací vždy stejný výsledek.
- Funkce nesmí měnit vstupní parametry a globální proměnné.
- Funkce nesmí měnit své chování na základě stavů definovaných mimo funkci.
Tyto principy se samozřejmě dají použít v jakémkoli typu programování, tedy i nejrozšířenějším OOP. A pokud se při vývoji používá TDD, nebo aspoň člověk zodpovědně píše unit testy, dojde k těmto principům zcela přirozeně.
Proč vlastně o něčem takovém (možná triviálním) píšu? Dopisoval jsem teď nedávno unit testy k hotové aplikaci a čas od času jsem narazil na kód, který porušoval všechny tři výše uvedené principy zároveň. Takový kód se samozřejmě velice špatně a také velice pracně testuje. Pro představu:
private void checkSMS() { if (sms == OperationEnum.ADDED) { if (servis24 == OperationEnum.ADDED || servis24 == OperationEnum.NOT_CHANGED) { codeOperationsList.add(OperationCodeEnum .FILING_SMS_WITH_S24 .getCode()); } else { codeOperationsList.add(OperationCodeEnum .FILING_SMS_WITHOUT_S24 .getCode()); } } }
Když to vezmu od "spoda":
- Funkce se rozhoduje na základě (globálních) flagů sms a servis24.
- Funkce mění globální/instanční proměnnou codeOperationList.
- Funkce vrací void. Tedy ať už dělá cokoliv, je to side efect.
No a jaké z toho všeho plyne poučení? Pište funkcionální metody! A pište unit testy! Aplikace pak půjde líp otestovat. Lépe spravovat. A po čase snadněji porozumíte kódu.
21. ledna 2011
Konečně kód!
Uf! Tak jsem se pěkně dlouho mordoval se syntax highlighting. Člověk by řekl, že když je Blogger od Googlu, že už by to mohl tak nějak defaultně umět. Nebo na to mít nějaký plugin. No, možná je to věc použité šablony - každopádně, skončil jsem její úpravou, kdy jsem do ní doplnil reference na SyntaxHighlighter, plus externí skript pro Clojure. Zkoušel jsem i google-code-prettify, ale nepodařilo se mi dosáhnout toho, aby to "prettyfájovalo".
Takže nějaký kód. Zatím jen trivialitky na okoštování a vyzkoušení highlightingu. Ale už se těším, až se ponořím do studia transakčních referencí, agentů a atomů :-)
Jen vysvětlení pro Javisty ;-) výše uvedený výstup je z prostředí REPL a pro pochopení lispovské syntaxe:
Java:
Lisp/Clojure:
Takže nějaký kód. Zatím jen trivialitky na okoštování a vyzkoušení highlightingu. Ale už se těším, až se ponořím do studia transakčních referencí, agentů a atomů :-)
(+ 1 2 3) ; 6 (defn hello [name] (println "Hello," name)) ; #'user/hello (hello "Guido") ; Hello, Guido ; nil (map + [1 2 3] [4 5 6]) ; (5 7 9)
Jen vysvětlení pro Javisty ;-) výše uvedený výstup je z prostředí REPL a pro pochopení lispovské syntaxe:
Java:
void processSomething(Type valueOne, Type valueTwo) { // some code } processSomething(valueOne, valueTwo);
Lisp/Clojure:
(defn process-something [valueOne valueTwo] ()) (process-something valueOne valueTwo)
20. ledna 2011
Clojure web-app
Hledal jsem na webu nějaké reference, nebo projekty, které by používaly Clojure a narazil jsem na zajímavou prezentaci od lidí, kteří vytvořili aplikaci TheDeadline. Jde o aplikaci pro GTD, která by měla obsahovat nějakou umělou inteligenci. Zajímavé jsou samozřejmě technologie, které se za touto aplikací skrývají.
Aplikace samotná běží na Google App Enginu (GAE) a původně používala Clojure jak pro komunikaci s (Google) Datastorem, tak pro zobrazení HTML. Jak jsem pochopil z jiné, pozdější prezentace, tak v současnosti by měli pro UI používat Google Closure Templates (další zajímavá technologie).
Datastrore v GAE nabízí dva druhy interfaců - high-level pro Java objekty a low-level, který umožňuje přímo pracovat s key-value strukturami. Což je přesně to, co nativně nabízí Clojure - StructMaps. Je to jedna z myšlenek, které mě v prezentaci nejvíc oslovily - pokud vytvářím webovou aplikaci v Javě, musím udělat (nebo použít framework) mapování tabulka-objekty (ORM) a pak pro prezentaci zase převod objekty-HTML. Tato dvojnásobná transformace, abych zobrazil data z databáze v prohlížeči, by při použití Clojure měla odpadnout, protože obojí by mělo jít přímo. Ono to samozřejmě tak jednoduché nebude, ale ta myšlenka se mi líbí.
Kromě této "technické" prezentace je zajímavá i druhá "manažerská" prezentace, která téma probírá spíše z designového hlediska (a zmiňuje se právě o Google Closure Templates).
A na závěr ještě rozhovor, který vyšel na InfoQ:
Aplikace samotná běží na Google App Enginu (GAE) a původně používala Clojure jak pro komunikaci s (Google) Datastorem, tak pro zobrazení HTML. Jak jsem pochopil z jiné, pozdější prezentace, tak v současnosti by měli pro UI používat Google Closure Templates (další zajímavá technologie).
Datastrore v GAE nabízí dva druhy interfaců - high-level pro Java objekty a low-level, který umožňuje přímo pracovat s key-value strukturami. Což je přesně to, co nativně nabízí Clojure - StructMaps. Je to jedna z myšlenek, které mě v prezentaci nejvíc oslovily - pokud vytvářím webovou aplikaci v Javě, musím udělat (nebo použít framework) mapování tabulka-objekty (ORM) a pak pro prezentaci zase převod objekty-HTML. Tato dvojnásobná transformace, abych zobrazil data z databáze v prohlížeči, by při použití Clojure měla odpadnout, protože obojí by mělo jít přímo. Ono to samozřejmě tak jednoduché nebude, ale ta myšlenka se mi líbí.
Kromě této "technické" prezentace je zajímavá i druhá "manažerská" prezentace, která téma probírá spíše z designového hlediska (a zmiňuje se právě o Google Closure Templates).
A na závěr ještě rozhovor, který vyšel na InfoQ:
18. ledna 2011
Sometimes Clojure
OK, Sometimes Clojure. Přišel čas naučit se další jazyk, a protože fandím jazykům, které běží v JVM, zalovil jsem primárně v těchto vodách. Kromě (mého) oblíbeného Groovy, které už pár let používám, jsem chtěl, aby to byl trošku "jiný" jazyk. A jelikož už nějakou dobu pošilhávám po funkcionálních jazycích, zvolil jsem Clojure. A zdá se, že je to láska na první pohled.
Proč trochu "jiný" jazyk? Představte si, že umíte celkem slušně anglicky, celkem slušně španělsky, cizí slova z latiny vám taky nejsou cizí a rozhodnete se pro francouzštinu. Z vlastní zkušenosti můžu říct, že francouzština je (zatím) nejtěžší evropský jazyk, který jsem se učil. Nicméně výše uvedené znalosti znamenají/způsobí, že učící křivka nového jazyka je velmi strmá. Co když se ale začnu učit nějaký neevropský jazyk? Jazyk, který vzniknul v jiné kultuře, postavený na jiných principech? Tak přesně to, takový neevropský jazyk, je pro mne momentálně Clojure.
Zatím jsem si Clojure nainstaloval v Linuxu i Win, vyzkoušel si základy a zkouknul pár prezentací. Doporučil bych:
- Clojure, kterou prezentuje jeho tvůrce Rich Hickey,
- Clojure for Java programmers (opět Rich Hickey),
- Casting Spells in Lisp (Clojure version) - hravý tutoriál, aneb naprogramujte si textovou hru v Clojure.
A závěrem, proč vznikl tenhle blog? V češtině toho zatím o Clojure moc není, jen sem tam pár zmínek, že něco takového existuje. Takže než se Clojure v Česku trochu rozšíří, třeba se mezitím stanu Clojure guru :-)
Přihlásit se k odběru:
Příspěvky (Atom)