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.

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:
(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))
; false
Více assertions/parametrů se dá testovat pomocí makra are:
(are [x y] (= 42 (+ x y))
     21 21
     20 22
     42 0)
; true
Vyhození 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 projektuWindows 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:
  • 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.
Takže, pojďme se podívat na ten příklad. Vytvořím soubor hello.cljs. Metadata ^:export u definice funkce říkají, aby název výsledné funkce nebyl kompilátorem minifikovaný.
(ns hello)

(defn ^:export greet [n]
    (str "Hello, " n "!"))
Následně ho zkompiluju:
cljsc hello.cljs {:optimizations :advanced} > hello.js
A 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.