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:
(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 :-)

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:
No a jak vypadá currying konkrétně? V Groovy:

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:
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.
Předchozí ukázka je samozřejmě z Javy, takže s/funkce/metoda/g.

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ů :-)
(+ 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: