<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>tag:plblog.danieljanus.pl,2019:category:clojure</id>
  <title>Daniel Janus – Clojure</title>
  <link href="http://plblog.danieljanus.pl/category/clojure/"/>
  <updated>2013-02-01T00:00:00Z</updated>
  <author>
    <name>Daniel Janus</name>
    <uri>http://danieljanus.pl</uri>
    <email>dj@danieljanus.pl</email>
  </author>
  <entry>
    <id>tag:plblog.danieljanus.pl,2013-02-01:post:tlumaczenia-technologiczne</id>
    <title>O książkach technologicznych i ich tłumaczeniach</title>
    <link href="http://plblog.danieljanus.pl/tlumaczenia-technologiczne/"/>
    <updated>2013-02-01T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Trudna rzecz jest jeden język drugim językiem dostatecznie wyrznąć.&lt;/em&gt;&lt;/p&gt;&lt;p style="text-align: right;"&gt;-- Jan Leopolita (1523--1572)&lt;/p&gt;
&lt;/blockquote&gt;&lt;h2 id="rodzaje-książek-informatycznych"&gt;Rodzaje książek informatycznych&lt;/h2&gt;&lt;p&gt;Mam zasadę dotyczącą książek informatycznych: nie kupuję niczego, co ma w tytule nazwę konkretnej technologii. (Purystów z góry przepraszam za użycie tego słowa, mając świadomość jego &lt;a href="https://groups.google.com/d/msg/pl.comp.programming/UH4hrQWBCPo/ILTtM_GEfO8J"&gt;pretensjonalności&lt;/a&gt;; ale nie znam innego polskiego określenia, które by zbiorczo obejmowało konkretne języki programowania, systemy operacyjne, platformy wykonawcze, serwery aplikacji, architektury itd.) Po pierwsze, technologie pojawiają się i znikają, wciąż ukazują się ich nowe wersje, i w efekcie czas życia książki opisującej, dajmy na to, konkretną implementację jakiegoś języka programowania na ogół bywa nie dłuższy niż kilka lat. Można wręcz mówić o „okresie półtrwania”, po którym połowa informacji zawartej w książce jest już nieaktualna.  Po drugie, i może ważniejsze, tego typu publikacje często wnoszą niewielką wartość dodaną w porównaniu do dokumentacji: od książki oczekuję, że nie tylko przekaże mi suche instrukcje, ale także idee, które w jakiś sposób wzbogacą mój warsztat i zostaną ze mną na dłużej. Dlatego do poduszki wolę poczytać o &lt;a href="http://www.amazon.co.uk/Kernel-Methods-Pattern-Analysis-Shawe-Taylor/dp/0521813972"&gt;uczeniu maszynowym za pomocą metod jądrowych&lt;/a&gt; niż o &lt;a href="http://www.amazon.co.uk/Pro-IBM-WebSphere-Application-Professionals/dp/1430219580"&gt;bebechach serwera aplikacyjnego&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Rzecz jasna, jak od każdej zasady, tak i od tej istnieją wyjątki. Bywa, że technologia na tyle wryła się w informatyczny krajobraz, że nie grozi jej szybkie przeterminowanie, a dana książka uznawana jest za kanoniczny opis technologii, nie zaś tylko dodatek &lt;em&gt;post hoc&lt;/em&gt; (jak &lt;a href="http://pl.wikipedia.org/wiki/J%C4%99zyk_ANSI_C"&gt;K&amp;R&lt;/a&gt; do C czy &lt;a href="http://en.wikipedia.org/wiki/Programming_Perl"&gt;Księga Wielbłądzia&lt;/a&gt; do Perla). Zdarza się też i tak, że książka opisuje język czy środowisko, którego akurat intensywnie używam lub przymierzam się do intensywnego używania, i wtedy mam szansę odnieść z niej dużą korzyść. Tak było z &lt;em&gt;&lt;a href="http://pragprog.com/book/shcloj/programming-clojure"&gt;Programming Clojure&lt;/a&gt;&lt;/em&gt; Stuarta Hallowaya, której pierwsze wydanie kupiłem niedługo po jego ukazaniu się z górą trzy lata temu, a drugie, zmienione i uaktualnione (a jakże!) wkrótce &lt;a href="http://helion.pl/ksiazki/programowanie-w-jezyku-clojure-stuart-halloway-aaron-bedra,proclo.htm"&gt;ukaże się po polsku&lt;/a&gt; nakładem &lt;a href="http://helion.pl/"&gt;Helionu&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Po przeczytaniu darmowej próbki nasunęły mi się wątpliwości odnośnie samego tłumaczenia, a w ślad za nimi myśli dotyczące polskich tłumaczeń literatury informatycznej w ogóle. Spróbuję je poniżej zebrać.&lt;/p&gt;&lt;h2 id="o-clojure-po-polsku"&gt;O Clojure po polsku&lt;/h2&gt;&lt;p&gt;Od razu powiem, że ogólny poziom przekładu Tomasza Walczaka uważam za wysoki: tekst jest spójny, nośny i pozbawiony błędów merytorycznych. Czepianie się szczegółów, które za chwilę nastąpi, ma być nie tyle krytyką, co raczej próbą uświadomienia sobie, dlaczego mimo wszystko „Programowanie w języku Clojure” czyta mi się mniej płynnie niż oryginał angielski; a także zwrócenia uwagi na problemy, z jakimi musi się zmierzyć tłumacz. Zasygnalizuję więc pewne zagadnienia, ale wcale nie mam pewności, czy potrafiłbym rozwiązać je lepiej, albo nawet – czy w ogóle się da.&lt;/p&gt;&lt;p&gt;Bo wydawałoby się, że tłumaczenie tekstu informatycznego jest czynnością bez porównania prostszą niż na przykład poezji, gdzie trzeba ważyć każde słowo, prawda? Tymczasem rodzi ono zupełnie nowe problemy. Tłumacz zmuszony jest często wymyślić od nowa terminologię z zakresu wąskiej dziedziny, o której po polsku się nie pisze lub pisze się rzadko lub nieformalnie; podjąć pewne nierzadko arbitralne decyzje i konsekwentnie się ich trzymać; albo – jak zobaczymy – zadecydować, co zrobić z subtelnymi różnicami odcieni znaczeniowych pewnych pojęć.&lt;/p&gt;&lt;h3 id="lispa-czy-lispu?"&gt;Lispa czy Lispu?&lt;/h3&gt;&lt;p&gt;Tłumacz często używa formy &lt;em&gt;Lispa&lt;/em&gt; jako dopełniacza od rzeczownika &lt;em&gt;Lisp&lt;/em&gt;. A jak naprawdę powinna brzmieć ta forma? Zdania na ten temat są podzielone. Wydaje się, że tradycyjnie w środowisku utrwalona była postać &lt;em&gt;Lispu&lt;/em&gt; (zapewne nawiązująca do odmiany nazw innych wczesnych języków programowania: &lt;em&gt;Fortranu&lt;/em&gt; i &lt;em&gt;COBOL-u&lt;/em&gt;), co potwierdza zarówno &lt;a href="http://poradnia.pwn.pl/lista.php?id=9081"&gt;poradnia językowa PWN&lt;/a&gt;, jak i słowniki: &lt;a href="http://sgjp.pl/"&gt;Słownik gramatyczny języka polskiego&lt;/a&gt; i słownik &lt;a href="http://www.sjp.pl/Lispu"&gt;sjp.pl&lt;/a&gt;. Forma &lt;em&gt;Lispa&lt;/em&gt; (być może urobiona przez analogię do odmiany nazw nowych języków: &lt;em&gt;Perla&lt;/em&gt;, &lt;em&gt;Pythona&lt;/em&gt;) też jest żywa w uzusie, choć, jak się zdaje, nieznacznie ustępuje odmianie &lt;em&gt;Lispu&lt;/em&gt; (w &lt;a href="http://nkjp.pl/"&gt;Narodowym Korpusie Języka Polskiego&lt;/a&gt; znajdujemy jej 19 wystąpień wobec 33 wystąpień &lt;em&gt;Lispu&lt;/em&gt;).&lt;/p&gt;&lt;p&gt;Dla kontrastu, słowo &lt;em&gt;Clojure&lt;/em&gt;, którego paradygmat odmiany (nieodmienne) nie budzi wątpliwości, pojawia się na ogół jako część sformułowania &lt;em&gt;język Clojure&lt;/em&gt;. Można by analogiczny zabieg przeprowadzić z &lt;em&gt;językiem Lisp&lt;/em&gt;. Albo pozostać przy formie &lt;em&gt;Lispu&lt;/em&gt;, która, choć zrazu brzmiała mi dziwnie, teraz wydaje się znacznie naturalniejsza niż &lt;em&gt;Lispa&lt;/em&gt;.&lt;/p&gt;&lt;h3 id="co-to-jest-instrukcja?"&gt;Co to jest instrukcja?&lt;/h3&gt;&lt;p&gt;Porównajmy taki fragment oryginału:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;defrecord&lt;/code&gt; and related functions are covered in Section 6.3, &lt;em&gt;Protocols&lt;/em&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;z odpowiadającym mu tłumaczeniem:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Instrukcję &lt;code&gt;defrecord&lt;/code&gt; i powiązane funkcje omawiamy w podrozdziale 6.3, „Protokoły”.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Tłumacz – słusznie – uznaje, że po polsku to zdanie brzmiałoby koślawo bez wprowadzenia na początku rzeczownika określającego, cóż to jest owo &lt;code&gt;defrecord&lt;/code&gt;. Wybiera słowo „instrukcja”, które nie budziłoby żadnych sprzeciwów, gdyby to była książka o Pythonie albo C.  Tu jednak mam wątpliwości. Wiele języków programowania odróżnia wyrażenia od instrukcji, i w nich te drugie rozumiane są jako autonomiczne fragmenty wykonywalnego kodu potencjalnie zmieniającego stan programu. A przecież Clojure kładzie duży nacisk na niezmienność danych! Co więcej, w Clojure, jak we wszystkich Lispach i wielu innych językach funkcyjnych, tak rozumianych instrukcji nie ma (albo patrząc z innego punktu widzenia: termin ten jest tożsamy z terminem „wyrażenie”) -- kod programu po prostu składa się z wyrażeń.&lt;/p&gt;&lt;p&gt;Jeśli więc nie instrukcja, to co? W angielskim &lt;a href="http://clhs.lisp.se/Body/26_a.htm"&gt;żargonie lispowym&lt;/a&gt; używane jest słowo &lt;em&gt;form&lt;/em&gt; na określenie obiektu, który ma być poddany ewaluacji. Jeśli ten obiekt jest listą, której pierwszy element jest symbolem (i w takim razie składnia i semantyka listy są wyznaczone przez ten symbol, który może nazywać funkcję, makro albo wbudowaną konstrukcję języka), to nazwy tego symbolu używa się często jako przydawki. Mówi się więc o „&lt;code&gt;defmacro&lt;/code&gt; forms”, „&lt;code&gt;defvar&lt;/code&gt; forms”, itd. To właśnie to określenie mógł tu pominąć Halloway. A jak jest po polsku &lt;em&gt;form&lt;/em&gt;? Znowu: nie ma powszechnej zgody. Można by to mechanicznie przełożyć na &lt;em&gt;forma&lt;/em&gt;, ale moim zdaniem nazwą mniej przeładowaną znaczeniowo i bardziej oddającą sens jest &lt;em&gt;formuła&lt;/em&gt;. W użyciu są obie wersje i w Google można znaleźć ich przykładowe wystąpienia.&lt;/p&gt;&lt;p&gt;(Na marginesie można oddać tłumaczowi sprawiedliwość, że Halloway tą elizją zdaje się sugerować, że &lt;code&gt;defrecord&lt;/code&gt; jest funkcją, gdy w rzeczywistości jest to makro. Z drugiej strony, makra w Clojure są w istocie funkcjami, których &lt;em&gt;vars&lt;/em&gt; (patrz niżej) mają ustawioną odpowiednią flagę, co powoduje specjalne traktowanie ich przez kompilator.)&lt;/p&gt;&lt;h3 id="zmienna-zmiennej-nierówna"&gt;Zmienna zmiennej nierówna&lt;/h3&gt;&lt;p&gt;Znowu zacznę od cytatu, tym razem najpierw dwóch kawałków tłumaczenia:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Nie występują tu zmienne, modyfikowalny stan ani rozgałęzienia.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;Przedrostek &lt;code&gt;#'&lt;/code&gt; oznacza, że funkcję zapisano w &lt;em&gt;zmiennej&lt;/em&gt; języka Clojure (...)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A tak wyglądają te fragmenty w oryginale:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;It has no variables, no mutable state, and no branches.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;The prefix &lt;code&gt;#'&lt;/code&gt; indicates that the function was stored in a Clojure &lt;em&gt;var&lt;/em&gt; (...)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;W polskim tłumaczeniu w obydwu tych fragmentach pojawia się słowo &lt;em&gt;zmienna&lt;/em&gt;.  Tymczasem spojrzenie na oryginał ujawnia, że słowo to ma dwa różne odcienie znaczeniowe, z których każdy oddawany jest po angielsku inaczej: raz jest to &lt;em&gt;variable&lt;/em&gt;, a raz &lt;em&gt;var&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Co to jest &lt;em&gt;variable&lt;/em&gt;, wiadomo z innych języków: nazwane „pudełko” przechowujące wartość. (Zauważmy, że Halloway używa tego słowa mówiąc o zmiennych w Javie.)  Jest to termin ogólny i używany w kontekście wszystkich języków imperatywnych, jak C czy Perl. Natomiast &lt;em&gt;var&lt;/em&gt; jest terminem węższym i oznacza &lt;em&gt;jedną z clojurowych implementacji&lt;/em&gt; takiego pudełka. Dokładniej mówiąc, jest to obiekt klasy &lt;code&gt;clojure.lang.Var&lt;/code&gt;. Obiekty takie mają dwie charakterystyczne cechy, nietypowe dla zmiennych z innych języków, a odróżniające je od atomów, również podobnych do zmiennych:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;wartości &lt;em&gt;var&lt;/em&gt; mogą być różne w różnych wątkach (przy czym istnieje koncepcja głównego wiązania, czyli wartości domyślnej, którą nowo utworzone wątki widzą dopóki jej sobie nie przesłonią własnym; takie wiązanie nie musi istnieć);&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;obiekty takie są pamiętane w przestrzeniach nazw: w istocie o przestrzeni nazw można myśleć jako o mapie wiążącej symbol z obiektem &lt;em&gt;var&lt;/em&gt;.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;(Taka dychotomia nazewnicza jest endemiczna dla Clojure i ma również miejsce przy funkcjach: w angielskim slangu clojurowym mówi się o &lt;em&gt;fns&lt;/em&gt;, czytane „ef-ens”, co oznacza obiekty implementujące interfejs &lt;code&gt;clojure.lang.IFn&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;To dopiero dylemat dla tłumacza! Nie mam pojęcia, co mógłbym tu zaproponować. Można by pozostać przy długim, doprecyzowującym określeniu „zmienna &lt;em&gt;języka Clojure&lt;/em&gt;” dla odróżnienia od „zmiennej” po prostu, ale ceną za to jest rozwlekłość. Nie wiem, czy można jej jakoś uniknąć.&lt;/p&gt;&lt;h3 id="insert-into-użytkownik-(imię)-values-(&amp;apos;judyto&amp;apos;)"&gt;INSERT INTO użytkownik (imię) VALUES ('Judyto')&lt;/h3&gt;&lt;p&gt;Ach, kod wypluwający komunikaty w języku naturalnym! Błogosławieni użytkownicy angielszczyzny, albowiem oni odmianą przez przypadki martwić się nie muszą. Halloway, na nieszczęście tłumacza, wpada na pomysł uogólnienia kanonicznego „Hello, world” vel „Witaj, świecie” poprzez sparametryzowanie witanego obiektu.&lt;/p&gt;&lt;p&gt;Chciałoby się, żeby funkcja witająca zwracała się do użytkownika w wołaczu. Tłumacz osiąga to bez problemu:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;hello&lt;/span&gt; [name]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;str&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;Witaj, &amp;quot;&lt;/span&gt; name))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I świetnie. Tylko, że trzeba wołać tę funkcję tak: &lt;code&gt;(hello "Janku")&lt;/code&gt;, co jest trochę nienaturalne. A co, jeśli oprócz wyświetlenia powitania chcemy imię zapisać też w bazie danych? Z drugiej strony, &lt;em&gt;licentia poetica&lt;/em&gt; tłumacza nie może sięgać tak daleko, żeby walnąć tu&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;witaj&lt;/span&gt; [imię]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;str&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;Witaj, &amp;quot;&lt;/span&gt; (&lt;span class="hljs-name"&gt;odmie&lt;/span&gt;ń imię &lt;span class="hljs-symbol"&gt;:wo&lt;/span&gt;łacz)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;!&lt;/p&gt;&lt;h2 id="warto,-nie-warto?"&gt;Warto, nie warto?&lt;/h2&gt;&lt;p&gt;Czy warto będzie kupić „Programowanie w języku Clojure” i przeczytać po polsku? Myślę, że tak, o ile ktoś jeszcze nie czytał po angielsku lub woli ojczysty język nawet kosztem drobnych nieścisłości. Jak już mówiłem, jestem pełen uznania dla pracy tłumacza i chylę czoła. Podziwiam!&lt;/p&gt;&lt;p&gt;Mam za to wątpliwości, czy warto &lt;em&gt;w ogóle tłumaczyć&lt;/em&gt; książki o technologiach. W pierwotnym zamyśle reszta niniejszej notki miała stanowić rozważania na ten temat, ale rozważania techniczne tak się rozrosły, że wpis zrobiłby się za długi. Więc o tym następnym razem.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2012-01-10:post:praktyczne-uzycie-monady-state</id>
    <title>Praktyczne użycie monady state</title>
    <link href="http://plblog.danieljanus.pl/praktyczne-uzycie-monady-state/"/>
    <updated>2012-01-10T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Wreszcie rozumiem monady!&lt;/p&gt;&lt;p&gt;Pierwszy raz zetknąłem się z nimi dobrych osiem lat temu, przy okazji nauki Haskella; wtedy jednak nie starczyło mi cierpliwości, aby zaznajomić się z podstawami teoretycznymi. Sprawy nie ułatwiał fakt, że trudno o &lt;em&gt;naprawdę&lt;/em&gt; przystępne i zrozumiałe wprowadzenie do tego tematu: wygląda na to, że każdy adept Haskella, zrozumiawszy monady, pisze na ten temat własny &lt;em&gt;tutorial&lt;/em&gt;. Ja się powstrzymam od tego naturalnego odruchu i po prostu odeślę do niesamowicie szczegółowego, ośmioczęściowego &lt;a href="http://mvanier.livejournal.com/3917.html"&gt;cyklu artykułów&lt;/a&gt; autorstwa Mike'a Vaniera, który -- wreszcie! – sprawił, że coś mi „zaskoczyło” w umyśle. Zamiast tego w tym wpisie wynotuję najważniejsze spostrzeżenia, jakie zapamiętałem, a potem pokażę dwie wersje pewnego kodu operującego na sekwencjach bitów: niemonadyczną i napisaną z użyciem monady &lt;code&gt;state&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Mike zakłada znajomość Haskella, jak pisze we wstępie, na poziomie typów polimorficznych i klas typów; w praktyce jednak myślę, że do zrozumienia tekstu wystarczy pewne obycie z haskellową notacją (bardzo zresztą przypominającą zwykłą notację matematyczną), bo bardziej zaawansowane rzeczy są wyjaśniane w tekście na bieżąco. Bardzo polecam.&lt;/p&gt;&lt;p&gt;Oto więc moje notatki:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Kluczowe dla zrozumienia monad jest pojęcie &lt;em&gt;funkcji monadycznych&lt;/em&gt;; znacznie łatwiej jest je sobie intuicyjnie wyobrazić niż monadyczne wartości, zwracane przez &lt;code&gt;m-return&lt;/code&gt;. Mike podaje pewne intuicje także dla tych drugich, ale dla mnie ważne jest, żeby myśleć o monadach w kategoriach (wzbogaconych) funkcji, a nie wartości przez nie zwracanych.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Łatwo wydefiniować przy użyciu prostego złożenia operacji &lt;code&gt;m-bind&lt;/code&gt; i &lt;code&gt;m-return&lt;/code&gt; funkcję składającą ze sobą dwie funkcje monadyczne. Przy użyciu tego operatora, który Mike nazywa &lt;code&gt;&lt;=&lt;&lt;/code&gt;, prawa rządzące monadami wyrażają się w elegancki i prosty do zapamiętania sposób: jest to operator łączny oraz &lt;code&gt;m-return&lt;/code&gt; jest jego lewą i prawą jedynką.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;I bodaj najistotniejsza rzecz – nie trzeba rozumieć monad, żeby ich używać. Niby oczywiste i niby tak właśnie pisałem do tej pory kod haskellowy, radośnie używając &lt;code&gt;do&lt;/code&gt;-notacji tak jakbym pisał kod imperatywny; ale okazuje się, że w ten sam sposób można z powodzeniem używać monady stanu i wyjaśnić, co ona robi, zupełnie w oderwaniu od całej reszty teorii monad. Wiedza na temat tego, co się dzieje „pod spodem”, jest konieczna tylko (i aż!) do zrozumienia, co poszczególne monady mają ze sobą wspólnego.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Reszta tego wpisu będzie poświęcona właśnie przykładowemu użyciu monady stanu w Clojure.  &lt;a href="http://plblog.danieljanus.pl/leniwa-wersja-makra"&gt;Pisałem wcześniej&lt;/a&gt;, że nie widziałem dotąd kodu, który dałby się wyrazić przejrzyściej zapisany z użyciem monad niż bez nich – i właśnie na taki kod się natknąłem.&lt;/p&gt;&lt;p&gt;Wyobraźmy więc sobie, że mamy strumień bitów. Dla prostoty zdefiniujmy sobie przykładowy strumień, na którym będziemy testować nasze funkcje, jako clojurowy wektor zer i jedynek.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;v&lt;/span&gt; [&lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Chcemy naddać naszemu strumieniowi pewne uporządkowanie: zdekodować zera i jedynki, które z niego przychodzą, według jakiegoś kodu o zmiennej długości. Najbardziej oczywistym, znanym każdemu kodem jest kod binarny: czytamy ze strumienia ileś bitów, traktujemy je jako binarną (bez znaku) reprezentację pewnej liczby i zwracamy tę liczbę. Łatwo napisać w Clojure funkcję, która to robi.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-binary&lt;/span&gt; [n bits]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;loop&lt;/span&gt;&lt;/span&gt; [res &lt;span class="hljs-number"&gt;0&lt;/span&gt; n n [fst &amp;amp; rst &lt;span class="hljs-symbol"&gt;:as&lt;/span&gt; bs] bits]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;zero?&lt;/span&gt;&lt;/span&gt; n)
      [res bs]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;recur&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; res res fst) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; n) rst))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Spróbujmy wczytać z naszego strumienia czterobitową liczbę:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;read-binary&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt; v)
&lt;span class="hljs-comment"&gt;;=&amp;gt; [14 (1 0 1 0 1 0)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Czternaście. Ale zauważmy, że zdekodowana liczba nie jest jedyną zwracaną wartością! Żeby dało się z naszego strumienia odczytywać dalsze liczby, musimy zwrócić parę (wektor dwuelementowy): wartość odczytana plus &lt;em&gt;reszta strumienia&lt;/em&gt;, z której będziemy czytać dalej. Piszemy wszak funkcyjnie: nie zmieniamy naszych wartości w miejscu, tylko z jednych wartości produkujemy następne.&lt;/p&gt;&lt;p&gt;Innym rodzajem kodu jest &lt;em&gt;kod unarny&lt;/em&gt;: czytamy po prostu ze strumienia jedynki, aż napotkamy pierwsze zero – wtedy przestajemy czytać i naszą wartością jest liczba przeczytanych bitów. W ten sposób dowolną liczbę dodatnią &lt;em&gt;n&lt;/em&gt; da się zareprezentować na &lt;em&gt;n&lt;/em&gt; bitach. Implementacja jest równie prosta:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-unary&lt;/span&gt; [bits]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;loop&lt;/span&gt;&lt;/span&gt; [n &lt;span class="hljs-number"&gt;1&lt;/span&gt; [fst &amp;amp; rst] bits]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;zero?&lt;/span&gt;&lt;/span&gt; fst)
      [n rst]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;recur&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;inc&lt;/span&gt;&lt;/span&gt; n) rst))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sprawdzamy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;read-unary&lt;/span&gt; v)
&lt;span class="hljs-comment"&gt;;=&amp;gt; [4 (1 0 1 0 1 0)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Trzy jedynki i zero, razem cztery skonsumowane bity, więc odczytaną liczbą jest 4. Działa.&lt;/p&gt;&lt;p&gt;Spróbujmy teraz skleić nasze funkcje, to znaczy odczytać ze strumienia dwie liczby: najpierw zakodowaną unarnie, a potem binarnie na sześciu bitach.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [[x v1] (&lt;span class="hljs-name"&gt;read-unary&lt;/span&gt; v)
      [y v2] (&lt;span class="hljs-name"&gt;read-binary&lt;/span&gt; &lt;span class="hljs-number"&gt;6&lt;/span&gt; v1)]
  [x y])
&lt;span class="hljs-comment"&gt;;=&amp;gt; [4 42]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wynik jest poprawny, ale kod nieelegancki: tak naprawdę nie interesują nas te wszystkie &lt;code&gt;v&lt;/code&gt;, &lt;code&gt;v1&lt;/code&gt;, &lt;code&gt;v2&lt;/code&gt;, które przepychamy przez nasze funkcje; potrzebujemy tylko przeczytanych liczb, a poszczególne części strumienia są nam tylko po to, żeby mieć z czego czytać. Im więcej składanych ze sobą operacji, tym łatwiej się pomylić.&lt;/p&gt;&lt;p&gt;Tu wkracza monada stanu, która umożliwia ukrycie tych pośrednich wartości i bardziej eleganckie złożenie &lt;code&gt;read-unary&lt;/code&gt; i &lt;code&gt;read-binary&lt;/code&gt;. Monadycznie zapisalibyśmy to jakoś tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;domonad&lt;/span&gt; state-m
         [x read-unary
          y (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;partial&lt;/span&gt;&lt;/span&gt; read-binary &lt;span class="hljs-number"&gt;6&lt;/span&gt;)]
         [x y])
&lt;span class="hljs-comment"&gt;;=&amp;gt; #&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wygląda to bardzo podobnie. &lt;code&gt;domonad state-m&lt;/code&gt; jest jak &lt;code&gt;let&lt;/code&gt;. Tak jak chcieliśmy, nie przekazujemy naszego stanu (strumienia) &lt;em&gt;explicite&lt;/em&gt;; zamiast tego przy &lt;code&gt;x&lt;/code&gt; i &lt;code&gt;y&lt;/code&gt; podajemy funkcje, które mają być zawołane na aktualnym stanie, żeby uzyskać żądaną wartość i nowy stan.&lt;/p&gt;&lt;p&gt;No dobrze, ale gdzie tu miejsce na nasz stan początkowy? Jak go przekazać? Proste: powyższa formuła, jak widać z mało czytelnego wyniku, zwróciła jakąś funkcję. Wystarczy ją teraz zawołać na naszym początkowym stanie, aby uzyskać wynik wraz ze stanem końcowym:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-name"&gt;*1&lt;/span&gt; v)
&lt;span class="hljs-comment"&gt;;=&amp;gt; [[4 42] nil]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jedyne, co wydaje się nadmiarowe w naszym złożeniu, to owo &lt;code&gt;partial&lt;/code&gt; pojawiające się wyżej. Jest to efekt uboczny faktu, że Clojure w odróżnieniu od Haskella rozróżnia funkcje jedno- i więcejargumentowe (tak dokładniej to w Haskellu występują wyłącznie funkcje jednoargumentowe). Skoro w &lt;code&gt;domonad state-m&lt;/code&gt; powinny na przemian pojawiać się symbole i funkcje jednoargumentowe biorące stan, to gdy nasza funkcja akceptuje coś jeszcze (tu: liczbę bitów), powinniśmy zrobić z niej funkcję jednoargumentową za pomocą &lt;code&gt;partial&lt;/code&gt;. Alternatywnie, jeżeli chcemy trzymać się stylu monadycznego, możemy przepisać &lt;code&gt;read-binary&lt;/code&gt; jako:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-binary&lt;/span&gt; [n]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [bits]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;loop&lt;/span&gt;&lt;/span&gt; [res &lt;span class="hljs-number"&gt;0&lt;/span&gt; n n [fst &amp;amp; rst &lt;span class="hljs-symbol"&gt;:as&lt;/span&gt; bs] bits]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;zero?&lt;/span&gt;&lt;/span&gt; n)
        [res bs]
        (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;recur&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; res res fst) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; n) rst)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Teraz zamiast &lt;code&gt;(partial read-binary 6)&lt;/code&gt; możemy napisać po prostu &lt;code&gt;(read-binary 6)&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Teraz, kiedy umiemy już składać operacje stanowe, zilustrujemy to przy pomocy jeszcze jednego kodu, a właściwie rodziny kodów nazywanych &lt;em&gt;kodami Golomba&lt;/em&gt;. Po szczegółowy opis odsyłam do &lt;a href="http://pl.wikipedia.org/wiki/Kod_Golomba"&gt;Wikipedii&lt;/a&gt;, natomiast dla potrzeb tego artykułu wystarczy nam wiedza, że te kody są parametryzowane jedną liczbą &lt;em&gt;m&lt;/em&gt;. Liczba w kodzie Golomba jest podzielona na dwie części, z których pierwsza jest zakodowana unarnie, a druga -- nie większa niż &lt;em&gt;m&lt;/em&gt; – binarnie. W implementacji możemy więc skorzystać z naszych gotowych już funkcji &lt;code&gt;read-unary&lt;/code&gt; i &lt;code&gt;read-binary&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Bez dłuższych już komentarzy przedstawię dwie implementacje: eksplicytną i używającą monady stanu. Obie będą używać pomocniczej funkcji &lt;code&gt;read-bit&lt;/code&gt;, czytającej po prostu jeden bit ze strumienia i używanej tak samo jak &lt;code&gt;read-unary&lt;/code&gt; czy &lt;code&gt;read-binary&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-bit&lt;/span&gt; [[bit &amp;amp; bits]]
  [bit bits])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Najpierw wersja niemonadyczna:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-golomb&lt;/span&gt; [m]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [bits]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [k (&lt;span class="hljs-name"&gt;clog2&lt;/span&gt; m)
          r (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;bit-shift-left&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; k) m)
          [a bits1] (&lt;span class="hljs-name"&gt;read-unary&lt;/span&gt; bits)
          [b bits2] ((&lt;span class="hljs-name"&gt;read-binary&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; k)) bits1)
          [ir bits] (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt; b r)
                      [b bits]
                      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [[nb bits] (&lt;span class="hljs-name"&gt;read-bit&lt;/span&gt; bits)]
                        [(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; b b nb (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; r)) bits]))]
      [(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;*&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; a) m) ir) bits])))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A teraz używająca monad. Żeby nie przeplatać &lt;code&gt;let&lt;/code&gt; i &lt;code&gt;domonad&lt;/code&gt;, można po prostu zdefiniować funkcję zwracającą pewną stałą wartość i niezmieniony stan – nazwę ją &lt;code&gt;m-const&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;m-const&lt;/span&gt; [x]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [state]
    [x state]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Używając jej, możemy zaimplementować &lt;code&gt;read-golomb&lt;/code&gt; tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;read-golomb-monadic&lt;/span&gt; [m]
  (&lt;span class="hljs-name"&gt;domonad&lt;/span&gt; state-m
           [k (&lt;span class="hljs-name"&gt;m-const&lt;/span&gt; (&lt;span class="hljs-name"&gt;clog2&lt;/span&gt; m))
            r (&lt;span class="hljs-name"&gt;m-const&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;bit-shift-left&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt; k) m))
            a read-unary
            b (&lt;span class="hljs-name"&gt;read-binary&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; k))
            nb (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt; b r) (&lt;span class="hljs-name"&gt;m-const&lt;/span&gt; &lt;span class="hljs-literal"&gt;nil&lt;/span&gt;) read-bit)]
           (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;*&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;dec&lt;/span&gt;&lt;/span&gt; a) m)
              (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt; b r) b (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; b b nb (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; r))))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Czytelniej?&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-07-21:post:clojurescript</id>
    <title>ClojureScript</title>
    <link href="http://plblog.danieljanus.pl/clojurescript/"/>
    <updated>2011-07-21T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Rich Hickey, autor Clojure, wyrasta na Steve'a Jobsa nowoczesnych języków programowania – przynajmniej jeśli idzie o sposób prezentacji swoich dzieł. Oto kilka dni temu na grupie dyskusyjnej Clojure pojawiło się &lt;a href="https://groups.google.com/forum/#!msg/clojure/v2fP1qQQBhQ/Xz836_GafIgJ"&gt;ogłoszenie&lt;/a&gt;, że Rich wystąpi na najbliższym spotkaniu nowojorskiej grupy użytkowników Clojure, że strumień wideo z wystąpienia będzie nadawany na żywo i że zaprezentowane zostanie „coś nowego”.&lt;/p&gt;&lt;p&gt;Tym czymś okazał się &lt;a href="https://github.com/clojure/clojurescript"&gt;ClojureScript&lt;/a&gt;: kompilator Clojure do JavaScriptu, łączący moc pierwszego języka z powszechnością drugiego.&lt;/p&gt;&lt;p&gt;JavaScript bywa nazywany „czarnym koniem” języków programowania, najbardziej niedocenianym językiem minionej dekady i najważniejszym obecnej. Istotnie, silniki JavaScriptu w przeglądarkach – a także wolnostojące, jak Node.js – poczyniły w ostatnich latach kolosalne postępy, jeśli chodzi o wydajność, narzędzie to jest coraz chętniej używane do programowania coraz poważniejszych aplikacji i nie mijając się z prawdą można powiedzieć, że JavaScript jest wszędzie.&lt;/p&gt;&lt;p&gt;A teraz te same słowa można odnieść również do Clojure. W tegorocznym &lt;a href="http://cemerick.com/2011/07/11/results-of-the-2011-state-of-clojure-survey/"&gt;sondażu&lt;/a&gt;, podobnie jak rok temu, społeczność Clojure najczęściej wskazywała JavaScript jako hipotetyczną alternatywną wobec JVM platformę, ale zapewne mało kto się spodziewał, że urzeczywistni się to tak szybko. Łatwo sobie uzmysłowić możliwości, jakie daje nowa implementacja. Ten sam kod na serwerze i w przeglądarce? Programowanie aplikacji iOS w Clojure? Jak najbardziej!&lt;/p&gt;&lt;p&gt;W ramach dodawania łyżki dziegciu do tej beczki miodu trzeba zauważyć jednak, że dialekt ClojureScript jest okrojony i ma nieco inną semantykę niż standardowy Clojure: nie ma STM, niezmienność struktur danych nie jest wymuszana, wektory są zwykłymi JavaScriptowymi tablicami. Czy to się zmieni, pokaże przyszłość.&lt;/p&gt;&lt;p&gt;Jaka przyszłość czeka Clojure i ClojureScript? Spróbuję pokusić się o przepowiednię:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Pojawienie się alternatywnej wobec JVM platformy wymusi ukształtowanie się bogatszej niż do tej pory biblioteki standardowej Clojure, wspólnej dla obu wersji. Ten proces trwa już teraz: tam, gdzie w czasach Clojure 1.0 pisało się &lt;code&gt;(.toUpperCase "foo")&lt;/code&gt; (wychodząc z założenia &lt;em&gt;where Java isn't broken, Clojure doesn't fix it&lt;/em&gt;), teraz kanoniczną formą jest &lt;code&gt;(clojure.string/upper-case "foo")&lt;/code&gt;. W ten sposób Clojure stopniowo stanie się językiem niezabawkowym według &lt;a href="http://www.3ofcoins.net/2009/01/30/common-lisp-clojure-and-seriousness/"&gt;definicji Maćka Pasternackiego&lt;/a&gt;.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;ClojureScript otworzy dla Clojure zupełnie nową niszę: stanie się popularny wśród programistów-frontendowców, piszących do tej pory głównie w JavaScripcie lub CoffeeScripcie.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Równocześnie pojawienie się ClojureScript nie oznacza, że zatrzymają się prace nad „tradycyjnym” Clojure. JVM pozostanie główną platformą rozwoju języka i to tam będą pojawiać się kolejne innowacyjne cechy. Wersja 1.3 jest już w fazie beta i nadanie jej statusu &lt;em&gt;final&lt;/em&gt; jest kwestią kilku tygodni.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Patrząc wstecz, aż trudno uwierzyć, jak dużą popularność zdobyło sobie Clojure. Gdyby ktoś mi cztery lata temu powiedział, że za cztery lata pojawi się nowy język z rodziny Lisp, który będzie znacznie bardziej elegancki niż Common Lisp, znacznie praktyczniejszy niż Scheme i używać go będą wielkie instytucje finansowe, wyśmiałbym go. A jednak: zaczynam wierzyć, że prawo Kopernika jednak nie stosuje się w informatyce.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-04-19:post:wrazenia-z-j4p-2011</id>
    <title>Wrażenia z java4people 2011</title>
    <link href="http://plblog.danieljanus.pl/wrazenia-z-j4p-2011/"/>
    <updated>2011-04-19T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Jest niedzielny poranek, siedzę w pociągu relacji Szczecin–Warszawa i właśnie wyciągnąłem laptopa, aby spisać wrażenia z trzeciej edycji konferencji &lt;a href="http://java4people.com"&gt;java4people&lt;/a&gt;, która odbyła się wczoraj w Szczecinie, póki jeszcze nie okrzepły i nie rozmyły się.&lt;/p&gt;&lt;p&gt;Zanim opowiem o poszczególnych wystąpieniach, winienem oddać zasłużone gratulacje organizatorom, czyli szczecińskiemu JUG-owi. Przestronne, nowoczesne aule Zachodniopomorskiego Uniwersytetu Technologicznego, dostępne przez cały czas ciastka i herbata, obiad (załapałem się na dokładkę!), after-party, a nade wszystko ciekawe prezentacje i rozmowy kuluarowe – wszystko to stwarzało znakomitą atmosferę sprzyjającą temu, aby posłuchać o nowych językach i technologiach wokół Javy i JVM oraz „dzielić się wiedzą i pasją”, jak głosi motto konferencji. Wielkie brawa!&lt;/p&gt;&lt;p&gt;Jedyny niedosyt polegał na tym, że prezentacje poprowadzone były w dwóch równoległych blokach, a do tego w trzecim bloku trwały warsztaty z SWT/JFace. Momentami bardzo doskwierał mi brak możliwości bilokacji, ale trudno winić o to organizatorów.&lt;/p&gt;&lt;p&gt;Oficjalnego otwarcia dokonał prof. Antoni Wiliński, dziekan Wydziału Informatyki ZUT, życząc uczestnikom owocnej wymiany doświadczeń. Następnie część ludzi udała się posłuchać wystąpienia Tomka Kopacza o .NET i Javie, ja zaś zadebiutowałem w roli prelegenta na imprezie tego formatu, opowiadając o Clojure.&lt;/p&gt;&lt;p&gt;Trudno mi wypowiadać się o własnej prezentacji – nie jestem dobrym mówcą i to &lt;a href="http://pacykarz.blogspot.com/2011/04/jawnosc-dla-ludu-s03e02-reminesencje.html"&gt;było&lt;/a&gt; &lt;a href="http://blog.kedziorski.pl/2011/04/18/java4people/"&gt;widać&lt;/a&gt;.  Jak się jednak okazało z rozmów ze słuchaczami, udało się zainteresować część osób tematem. Sukces jest więc połowiczny, a postaram się, aby następnym razem było lepiej. Slajdy można znaleźć &lt;a href="http://danieljanus.pl/slides/j4p/"&gt;tu&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Potem wybrałem się na wystąpienie Przemka Pokrywki o Scali. Scala bywa wymieniana jednym tchem z Clojure, gdy mowa o nowych językach funkcyjnych działających na JVM, więc byłem bardzo ciekaw tego wykładu. I nie zawiodłem się: z Przemka wprost emanowała pasja, znać było też dużą wiedzę i doświadczenie w temacie. Przemek ciekawie pokazał, w jaki sposób Scala łączy paradygmat funkcyjny z obiektowym -- w tym drugim zakresie przypomina mi Smalltalka: tu też wszystko jest obiektem i &lt;code&gt;2 + 3&lt;/code&gt; oznacza zawołanie na obiekcie &lt;code&gt;2&lt;/code&gt; metody &lt;code&gt;+&lt;/code&gt; z parametrem &lt;code&gt;3&lt;/code&gt;.  Fajnym mechanizmem wydaje się też możliwość definiowania własnych niejawnych konwersji – to sposób na wzbogacanie już istniejących klas o nowe funkcjonalności, ale bez zmiany tych klas; przypomina to trochę Clojurowe protokoły i &lt;code&gt;extend-type&lt;/code&gt;. Dopisuję do listy rzeczy do wypróbowania.&lt;/p&gt;&lt;p&gt;Po obiedzie, zachęcony tytułem, wybrałem się na prezentację „Skalowalność technologii Javowych w zastosowaniach komercyjnych” Dawida Gruszczyńskiego. Organizatorzy konferencji na samym początku rozdali ankiety, w których prosili o ocenienie każdego wystąpienia w skali od 1 (szkoda czasu) do 5 (naprawdę warto) – po tej prezentacji dały się słyszeć rozmowy, że należałoby dać jej 0 albo i -1. Ja jestem łagodniejszy w swych ocenach. Bezsprzecznie prezentacja była źle zatytułowana: tytuł obiecywał opowieść o technikach czy narzędziach projektowania skalowalnych aplikacji; tymczasem okazało się to klasycznym &lt;em&gt;success story&lt;/em&gt; dotyczącym systemu obsługi spisu powszechnego, w tym oprogramowania działającego na terminalach używanych przez rachmistrzów.&lt;/p&gt;&lt;p&gt;Czy ta prezentacja była nie na miejscu na takiej konferencji? Może i tak. Mam jednak słabość do &lt;em&gt;success stories&lt;/em&gt; i duży szacunek do ludzi, którym udaje się stworzyć działające, niezawodne oprogramowanie, nawet jeśli nie zgadzam się z wyborem technologii. To naprawdę spora rzecz. A samej prelekcji trzeba oddać, że była sprawnie przeprowadzona, slajdy zrozumiałe, a diagramy prezentujące architekturę systemu – czytelne. Wątpliwości słuchaczy budziła kwestia bezpieczeństwa transmisji danych (i samych rachmistrzów!) Ciekawostka: terminale rachmistrzowskie mają zmienne IP, ale nie powiadamiają serwera co chwila o swoim aktualnym adresie, bo wywoływałoby to zanadto duże obciążenie. Co więc robi terminal, gdy zmieni mu się adres? Otóż... wysyła SMS (!) ze swoim aktualnym adresem!&lt;/p&gt;&lt;p&gt;Na wystąpieniu Sebastiana Pietrowskiego o Jythonie (a także samym Pythonie i Django) nie dowiedziałem się wiele nowego, z racji wcześniejszych swoich doświadczeń z Pythonem. Ale też celem tej prezentacji było chyba nie tyle przekazanie wiedzy, co podzielenie się własnymi doświadczeniami, z subiektywnego punktu widzenia – i to udało się Sebastianowi znakomicie. Stąd slajdy zatytułowane „What I Like”, „What I Don’t Like”, „What I’d Use Python For” – bardzo pragmatyczne. Sebastian opowiadał między innymi o tym, że Jythona można fajnie wykorzystać jako narzędzie do eksploracji nowych API z REPL-a: używając pythonowej introspekcji można łatwo obejrzeć listę pól i metod udostępnianych przez dowolną klasę. W swoim wystąpieniu o tym nie opowiadałem, ale w pakiecie repl-utils w Clojure Contrib jest funkcja &lt;code&gt;show&lt;/code&gt;, która daje bardzo podobny efekt.&lt;/p&gt;&lt;p&gt;Słuchając wystąpienia Bartka Kuczyńskiego o Vaadinie utwierdziłem się w przekonaniu, że nie chcę pisać aplikacji webowych tak jak okienkowe, na modłę swingową.  Vaadin ma w zamierzeniu służyć właśnie do tego: to nadbudowany nad GWT modularny framework WWW.  Bartek pokazywał dziejącą się pod spodem magię, wraz z protokołem, którym część kliencka kompilowana do JS komunikuje się z serwerową (UIDL – dobrze pamiętam? – i JSON z danymi do wyświetlenia, poprzedzonymi ni mniej ni więcej tylko pętlą nieskończoną &lt;code&gt;for(;;);&lt;/code&gt;, co podobno ma jakiś sens). Psikus w tym, że ostatnio w &lt;a href="http://smyrna.danieljanus.pl"&gt;Smyrnie&lt;/a&gt; zrobiłem dokładnie odwrotną rzecz, to znaczy napisałem aplikację &lt;em&gt;desktopową&lt;/em&gt; tak jakbym pisał webową, a więc używając ajaksowego XML-RPC (odsyłam do niedawnego &lt;a href="http://plblog.danieljanus.pl/smyrna"&gt;postu na ten temat&lt;/a&gt;), i wyszło na oko prościej niż w Vaadinie. Może opowiem o tym na Warszawa JUG?&lt;/p&gt;&lt;p&gt;Nie zdążyłem się obejrzeć, a to już koniec konferencji! Do dotychczasowych bonusów dołączył kolejny: wśród wypełniających ankiety organizatorzy rozlosowali pięć książek. Los wytypował mnie jeszcze raz i schowałem do plecaka „Domain-Specific Languages” Martina Fowlera.&lt;/p&gt;&lt;p&gt;Pokonferencyjne after-parties bywają bodaj ważniejszymi nawet elementami takich spotkań niż same prezentacje. To tam toczy się najwięcej rozmów, to tam zawiązują się nowe znajomości i trwają nieskrępowane, długie dyskusje. Tak bywa z pizzą na &lt;a href="http://aulapolska.pl"&gt;Auli&lt;/a&gt;, tak było i teraz. Wielkie dzięki raz jeszcze organizatorom, wszystkim, których poznałem, z którymi udało mi się porozmawiać, i tym, którzy przebrnęli przez moje wystąpienie.  Do zobaczenia przy innej okazji!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-02-12:post:smyrna</id>
    <title>Smyrna</title>
    <link href="http://plblog.danieljanus.pl/smyrna/"/>
    <updated>2011-02-12T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Dziś chciałbym zaprezentować kolejny stworzony przeze mnie przykład wykorzystania Clojure w praktyce – program &lt;a href="http://smyrna.danieljanus.pl"&gt;Smyrna&lt;/a&gt;. To proste narzędzie do przeszukiwania korpusów: umożliwia łatwe zindeksowanie zbioru dokumentów w formacie HTML, wyszukanie wystąpień interesującego nas leksemu i stworzenie listy frekwencyjnej słów.&lt;/p&gt;&lt;img src="/img/blog/smyrna.png"&gt;
&lt;p&gt;Program zrodził się z potrzeby chwili (potrzebowałem porównać wystąpienia pewnej grupy słów w różnych zestawach danych), ale uznałem, że jest na tyle użyteczny, że może przydać się nie tylko mnie. Zwłaszcza że nie było dotąd programu na wolnej licencji, który by umożliwiał &lt;em&gt;łatwe&lt;/em&gt; przeszukiwanie własnych zbiorów polskich tekstów. Można wykorzystać &lt;a href="http://poliqarp.sf.net"&gt;Poliqarpa&lt;/a&gt;, w którym też maczałem palce (a raczej nurzałem ręce), jednak używanie go z własnymi zbiorami danych wymaga ekwilibrystyki w stylu &lt;a href="http://github.com/nathell/dxces"&gt;dxces&lt;/a&gt; i jest trudne do przeskoczenia dla nietechnicznych użytkowników. Jest więc Smyrna swoistym uzupełnieniem dla Poliqarpa – &lt;a href="http://pl.wikipedia.org/wiki/Polikarp_ze_Smyrny"&gt;stąd zresztą nazwa&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Z technicznego punktu widzenia interesujący może być sposób, w jaki skonstruowałem interfejs użytkownika. Mimo że Smyrna jest w zasadzie aplikacją desktopową, obsługuje się ją przez przeglądarkę WWW: uruchamiany jest lokalny serwerek HTTP na porcie 8080, a następnie przeglądarka z tym adresem; cała dalsza komunikacja między JavaScriptowym kodem klienckim a Clojurowym silnikiem przeszukującym odbywa się przez JSON-RPC.&lt;/p&gt;&lt;p&gt;W ramach ćwiczenia, kliencki kod napisałem w nowej, alternatywnej składni dla JavaScriptu, czyli zyskującym coraz większą popularność &lt;a href="http://jashkenas.github.com/coffee-script/"&gt;CoffeeScripcie&lt;/a&gt;. Eksperyment uważam za udany: kod wychodzi czytelniejszy i zwięźlejszy niż w "zwykłym" JS.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-01-31:post:czyszczenie-dowiazan-lokalnych</id>
    <title>Clojure: czyszczenie dowiązań lokalnych</title>
    <link href="http://plblog.danieljanus.pl/czyszczenie-dowiazan-lokalnych/"/>
    <updated>2011-01-31T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;O jednej z nowych cech Clojure 1.2 dowiedziałem się dopiero niedawno, bo jej wprowadzenie przeszło właściwie bez echa. Może dlatego, że to optymalizacja niewidoczna na zewnątrz; jednak rozwiązuje ważny problem i dlatego warto mieć świadomość jej istnienia. Chodzi o &lt;em&gt;locals clearing&lt;/em&gt;, które tłumaczę na polski jako „czyszczenie lokalnych dowiązań”.&lt;/p&gt;&lt;p&gt;Wyobraźmy sobie taką sytuację: mamy do wykonania skomplikowane obliczenie. Możemy je podzielić na mniejsze kroki; na każdym etapie wyliczamy nowy pośredni wynik na podstawie tylko niewielu ostatnich kroków – jednego, może dwóch. Załóżmy jeszcze, że te pośrednie wyniki są dużych rozmiarów: powiedzmy setek megabajtów. To częsty przypadek, kiedy wykonuje się np. obliczenia na dużych, gęstych macierzach: najpierw normalizujemy każdy z wektorów naszej macierzy, potem ją transponujemy, następnie liczymy jej rozkład własny i odrzucamy część wektorów własnych.&lt;/p&gt;&lt;p&gt;W Javie wyglądałoby to tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs java"&gt;&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title function_"&gt;calculate&lt;/span&gt;&lt;span class="hljs-params"&gt;(Matrix m)&lt;/span&gt; {
    &lt;span class="hljs-type"&gt;Matrix&lt;/span&gt; &lt;span class="hljs-variable"&gt;m1&lt;/span&gt; &lt;span class="hljs-operator"&gt;=&lt;/span&gt; step1(m);
    &lt;span class="hljs-type"&gt;Matrix&lt;/span&gt; &lt;span class="hljs-variable"&gt;m2&lt;/span&gt; &lt;span class="hljs-operator"&gt;=&lt;/span&gt; step2(m1);
    &lt;span class="hljs-comment"&gt;// i tak dalej, każdy krok odwołuje się do poprzedniego&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jest to jednak prosta droga do pojawienia się &lt;code&gt;OutOfMemoryError&lt;/code&gt;, o ile nie dysponujemy wystarczającą ilością pamięci, aby pomieścić &lt;em&gt;wszystkie&lt;/em&gt; kroki. Gdy &lt;code&gt;m2&lt;/code&gt; jest obliczone, wartość &lt;code&gt;m1&lt;/code&gt; jest już niepotrzebna i w zasadzie można by zwolnić zajmowaną przez nią pamięć. Odśmiecacz jednak tego nie zrobi, dopóki wywołanie funkcji &lt;code&gt;calculate&lt;/code&gt; nie zakończy się, ponieważ do tej pamięci odwołuje się ramka na stosie wywołań.&lt;/p&gt;&lt;p&gt;Zilustrujmy to uruchamialnym przykładem:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs java"&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;foo&lt;/span&gt; {
    &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;static&lt;/span&gt; &lt;span class="hljs-type"&gt;byte&lt;/span&gt;[] calculate(&lt;span class="hljs-type"&gt;byte&lt;/span&gt;[] lastStep) {
        &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-title class_"&gt;byte&lt;/span&gt;[&lt;span class="hljs-number"&gt;10485760&lt;/span&gt;];
    }

    &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;static&lt;/span&gt; &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title function_"&gt;main&lt;/span&gt;&lt;span class="hljs-params"&gt;(String... args)&lt;/span&gt; {
        &lt;span class="hljs-type"&gt;byte&lt;/span&gt;[] a, b, c, d;
        a = calculate(&lt;span class="hljs-literal"&gt;null&lt;/span&gt;);
        b = calculate(a);
        c = calculate(b);
        d = calculate(c);
        System.out.println(&lt;span class="hljs-string"&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Funkcja &lt;code&gt;calculate&lt;/code&gt; emuluje skomplikowane obliczenia, alokując za każdym razem 10-megabajtowy blok pamięci. Jeśli skompilujemy ten program i uruchomimy go w środowisku z maksymalnym rozmiarem sterty ograniczonym do 30 MB (&lt;code&gt;java -Xmx30M foo&lt;/code&gt;), zobaczymy błąd braku pamięci.&lt;/p&gt;&lt;p&gt;Inaczej jest w Clojure. Odpowiednik powyższego programu wygląda tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;calculate&lt;/span&gt; [last-step]
  (&lt;span class="hljs-name"&gt;make-array&lt;/span&gt; Byte/TYPE &lt;span class="hljs-number"&gt;10485760&lt;/span&gt;))

(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;-main&lt;/span&gt; [&amp;amp; args]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [a (&lt;span class="hljs-name"&gt;calculate&lt;/span&gt; &lt;span class="hljs-literal"&gt;nil&lt;/span&gt;)
        b (&lt;span class="hljs-name"&gt;calculate&lt;/span&gt; a)
        c (&lt;span class="hljs-name"&gt;calculate&lt;/span&gt; b)
        d (&lt;span class="hljs-name"&gt;calculate&lt;/span&gt; c)]
    (&lt;span class="hljs-name"&gt;println&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;W Clojure 1.2 i nowszych ten program wypisze &lt;code&gt;OK&lt;/code&gt;, nawet gdy będzie miał do dyspozycji tylko 30 MB pamięci.&lt;/p&gt;&lt;p&gt;Dlaczego to działa? Okazuje się, że kompilator Clojure przeprowadza statyczną analizę każdego kawałka kodu, w którym wartości są dowiązane do nazw (czyli każdego użycia formuły &lt;code&gt;let&lt;/code&gt;), i dla każdego dowiązania sprawdza, w którym miejscu jest ono ostatni raz używane, biorąc pod uwagę wszystkie możliwe ścieżki wykonania. Natychmiast po ostatnim użyciu emitowany jest bajtkod zerujący odpowiednią zmienną (ustawiający wskaźnik na &lt;code&gt;null&lt;/code&gt;). Innymi słowy, efekt jest taki, jakbyśmy w przykładowym kodzie w Javie wstawili instrukcję &lt;code&gt;a = null;&lt;/code&gt; zaraz po obliczeniu &lt;code&gt;b&lt;/code&gt; i analogicznie dalej.&lt;/p&gt;&lt;p&gt;O jednej z sytuacji, w których takie czyszczenie się przydaje, już powiedziałem. Innym częstym przypadkiem jest branie za punkt wyjścia długiej (być może nawet nieskończonej) leniwej sekwencji, która jest realizowana w kolejnych krokach obliczeń. Dzięki automatycznemu czyszczeniu odśmiecacz może pozbyć się pierwszych, niepotrzebnych już elementów zrealizowanej sekwencji, co umożliwia pisanie klarownego kodu bez troszczenia się o jego poprawność pamięciową.&lt;/p&gt;&lt;p&gt;Jak jednak &lt;a href="https://groups.google.com/d/msg/clojure/Xmu3pUMgiJk/Lx1Sq462h4wJ"&gt;napisał mi Rich Hickey&lt;/a&gt;, sama alokacja pamięci w momencie wiązania nazwy &lt;em&gt;nie jest&lt;/em&gt; uważana za użycie tej nazwy. Stąd jeśli nazwa nigdy nie zostanie użyta, to odpowiadająca jej wartość nie zostanie też odśmiecona aż do opuszczenia ciała bloku &lt;code&gt;let&lt;/code&gt;. Dlatego też nie mogłem pominąć w powyższym przykładzie argumentu &lt;code&gt;last-step&lt;/code&gt; funkcji &lt;code&gt;calculate&lt;/code&gt;.  Jednak taki przypadek nie pojawia się w praktyce: wszak jeśli wyliczamy jakiś wynik, to nie po to, by go do niczego nie użyć!&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-01-19:post:pokoloruj-sobie-europe</id>
    <title>Pokoloruj sobie Europę!</title>
    <link href="http://plblog.danieljanus.pl/pokoloruj-sobie-europe/"/>
    <updated>2011-01-19T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;To jest druga część minicyklu zapoczątkowanego &lt;a href="http://plblog.danieljanus.pl/zippery-w-clojure"&gt;artykułem o zipperach&lt;/a&gt;. Temat ten zaprezentowałem również podczas wystąpienia na spotkaniu &lt;a href="http://warszawa.jug.pl"&gt;Warszawa JUG&lt;/a&gt;. Dostępne są &lt;a href="http://danieljanus.pl/slides/2010/wjug-clojure/"&gt;slajdy z tej prezentacji&lt;/a&gt; (&lt;em&gt;uwaga:&lt;/em&gt; slajdy mają postać oskryptowanego HTML; należy je oglądać w Firefoksie lub którejś przeglądarce WebKitowej). Można również obejrzeć &lt;a href="http://jaceklaskowski.blip.tv/file/4573404/"&gt;nagranie wideo&lt;/a&gt; wystąpienia, niestety ze słabą jakością dźwięku.&lt;/p&gt;&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;&lt;p&gt;Jakiś czas temu zostałem poproszony o sporządzenie kilku mapek Europy pokolorowanych w różny sposób. Dostałem kilka zestawów danych, w których każdemu krajowi UE przypisana była jakaś liczba: im większa ta wartość, tym ciemniejszym odcieniem państwo miało być zaznaczone na mapce. Przykładowa pokolorowana mapka miała wyglądać tak:&lt;/p&gt;&lt;img src="/img/blog/europa.png"&gt;
&lt;p&gt;Rozpocząłem od ściągnięcia łatwo edytowalnej &lt;a href="http://commons.wikimedia.org/wiki/File:Blank_map_of_Europe.svg"&gt;mapy&lt;/a&gt; ze stron Wikimedia Commons, obliczyłem potrzebne intensywności kolorów dla pierwszego zestawu, uruchomiłem &lt;a href="http://www.inkscape.org"&gt;Inkscape&lt;/a&gt; i zabrałem się do kolorowania. Po półgodzinie żmudnego klikania zdałem sobie sprawę, że prościej i szybciej byłoby mi napisać programik w Clojure, który wygeneruje mapkę za mnie. Okazało się to zadaniem dość prostym: reszta tego postu będzie rekonstrukcją przedsięwziętych przeze mnie kroków.&lt;/p&gt;&lt;h2 id="svg"&gt;SVG&lt;/h2&gt;&lt;p&gt;Formatem źródłowego obrazka jest SVG. Wiedziałem, że to XML-owy otwarty format grafiki wektorowej, często widywałem na Wikipedii obrazy w tym formacie – jednak jego ręczna edycja to coś, z czym nie miałem do tej pory do czynienia. Szczęśliwie okazało się, że obrazek z mapkami ma prostą strukturę. Krzywa-obwiednia każdego kraju jest opisana elementem &lt;code&gt;path&lt;/code&gt;, wyglądającym mniej więcej tak:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs xml"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;path&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;id&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;class&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;eu europe&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;d&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;współrzędne kolejnych węzłów krzywej (bardzo dużo)&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Warto zwrócić uwagę na atrybut &lt;code&gt;id&lt;/code&gt; – jest to dwuliterowy skrót ISO-3166-1-ALPHA2 nazwy kraju. Na początku kodu obrazka znajduje się zresztą komentarz, który wyjaśnia użyte konwencje nazewnicze. Dysponowanie tak porządnie przygotowanymi danymi wejściowymi znakomicie ułatwiło mi pracę.&lt;/p&gt;&lt;p&gt;Jak się okazuje, SVG, podobnie jak HTML, &lt;a href="http://www.w3.org/TR/SVG/styling.html"&gt;używa stylów CSS&lt;/a&gt; do definiowania wyglądu elementów. Wszystko, co trzeba zrobić do nadania Polsce koloru czerwonego, to nadanie odpowiedniej wartości atrybutowi stylu &lt;code&gt;fill&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs xml"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;path&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;id&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;style&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;fill: #ff0000;&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;class&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;eu europe&amp;quot;&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;d&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;współrzędne kolejnych węzłów krzywej (bardzo dużo)&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Uzbrojeni w taką wiedzę, możemy rozpocząć pracę.&lt;/p&gt;&lt;h2 id="xml-w-clojure"&gt;XML w Clojure&lt;/h2&gt;&lt;p&gt;Podstawowym sposobem obsługi XML w Clojure jest wykorzystanie biblioteki &lt;code&gt;clojure.xml&lt;/code&gt;, pozwalającej na parsowanie XML-a (na zasadzie DOM, tzn. do struktury w pamięci) oraz serializację, tzn. działanie w odwrotną stronę. Uruchommy więc REPL i zacznijmy od wczytania naszej mapki i sparsowania jej:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;use&lt;/span&gt;&lt;/span&gt; &amp;#x27;clojure.xml)
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&amp;gt; (&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;m&lt;/span&gt; (&lt;span class="hljs-name"&gt;parse&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;/home/nathell/eur/Blank_map_of_Europe.svg&amp;quot;&lt;/span&gt;))
[...dłuuugie oczekiwanie...]
Unexpected end of file from server
  [Thrown class java.net.SocketException]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Zaraz! Jaki znowu &lt;code&gt;SocketException&lt;/code&gt;? Firefox wyświetla tę mapkę poprawnie, Chromium też, WTF? W tak świetnym języku, jakim jest Clojure, wszystko powinno działać od ręki?!&lt;/p&gt;&lt;p&gt;Cóż, język jest tak dobry, jak dobre są jego biblioteki – a jeśli chodzi o Clojure, można to rozwinąć: biblioteki Clojure są tak dobre, jak dobre są biblioteki Javy, których używają pod spodem. W tym wypadku natrafiliśmy na cechę standardowego (z pakietu &lt;code&gt;javax.xml&lt;/code&gt;) parsera XML w Javie. Jest on restrykcyjny i stara się odrzucać dokumenty, które nie są poprawne (w sensie &lt;em&gt;valid&lt;/em&gt;, a nie tylko &lt;em&gt;well-formed&lt;/em&gt;). Jeśli tylko dany plik zawiera deklarację &lt;code&gt;DOCTYPE&lt;/code&gt;, to parser javowy, a za nim &lt;code&gt;clojure.xml/parse&lt;/code&gt;, próbuje ściągnąć z podanego adresu schemat DTD i zwalidować dokument według tego schematu. Jest to działanie z wielu względów niepożądane, zwłaszcza z punktu widzenia &lt;a href="http://www.w3.org"&gt;World Wide Web Consortium&lt;/a&gt;, bo to na serwerach tej organizacji przechowywane są schematy opisujące sieciowe standardy. Łatwo sobie wyobrazić, jak duży ruch sieciowy generują nadgorliwe parsery: traktuje o tym &lt;a href="http://www.w3.org/blog/systeam?cat=68"&gt;post na blogu W3C&lt;/a&gt;. Zapewne wielu programistów Javy zetknęło się kiedyś z tym problemem. Jest kilka rozwiązań; użyjemy najprostszego z nich, czyli po prostu... ręcznie usuniemy przeszkadzającą deklarację &lt;code&gt;DOCTYPE&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;m&lt;/span&gt; (&lt;span class="hljs-name"&gt;parse&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;/home/nathell/eur/bm.svg&amp;quot;&lt;/span&gt;))
#&amp;#x27;user/m
&amp;gt; m
[...wiele ekranów liczb...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;No, tym razem udało się wczytać. Obejrzenie sparsowanej struktury jest utrudnione z uwagi na jej wielkość (można było się tego spodziewać: plik SVG waży ponad 0,5 MB!), ale z samego początku tego, co nam wypluwa REPL, widzimy, że jest to mapa. Zobaczmy, z jakich kluczy się składa:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;keys&lt;/span&gt;&lt;/span&gt; m)
(&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:content&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jest to więc mapa o trzech polach: z nazw (albo z dokumentacji) można się domyślić, co zawierają. Pole &lt;code&gt;:tag&lt;/code&gt; to nazwa elementu XML, &lt;code&gt;:attrs&lt;/code&gt; jest mapą atrybutów tego elementu, a &lt;code&gt;:content&lt;/code&gt; – sekwencją jego podelementów, z których każdy jest znów reprezentowany przez mapę o takiej samej strukturze (względnie przez napis, jeśli jest to element napisowy):&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; m)
&lt;span class="hljs-symbol"&gt;:svg&lt;/span&gt;
&amp;gt; (&lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; m)
{&lt;span class="hljs-symbol"&gt;:xmlns&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;http://www.w3.org/2000/svg&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:width&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;680&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:height&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;520&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:viewBox&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;1754 161 9938 7945&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:version&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:id&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;svg2&amp;quot;&lt;/span&gt;}
&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;count&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; m))
&lt;span class="hljs-number"&gt;68&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tak dla wprawki, spróbujmy zapisać sparsowaną reprezentację z powrotem jako XML. Funkcja &lt;code&gt;emit&lt;/code&gt; powinna być w stanie to zrobić, ale wypisujekl ona XML na standardowe wyjście. Możemy użyć makra &lt;code&gt;with-out-writer&lt;/code&gt; z pakietu &lt;code&gt;clojure.contrib.io&lt;/code&gt;, aby zrzucić XML-a do pliku:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;use&lt;/span&gt;&lt;/span&gt; &amp;#x27;clojure.contrib.io)
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&amp;gt; (&lt;span class="hljs-name"&gt;with-out-writer&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;/tmp/a.xml&amp;quot;&lt;/span&gt; (&lt;span class="hljs-name"&gt;emit&lt;/span&gt; m))
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Próbujemy otworzyć wynikowy obrazek w Firefoksie i...&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Błąd parsowania XML: nieprawidłowo sformowany
Obszar: file:///tmp/a.xml
Numer linii: 15, kolumna 44: Updated to reflect dissolution of Serbia &amp;amp; Montenegro: http://commons.wikimedia.org/wiki/User:Zirland
----------------------------------------------------------------------^
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Argh! Wygląda na to, że znaleźliśmy błąd w &lt;code&gt;clojure.xml&lt;/code&gt;: elementy zawierające ampersandy nie są poprawnie serializowane, przez co późniejsze parsowanie się nie powodzi. Zgłosimy ten błąd później, a teraz usuńmy znowu doraźnie wskazaną przez Firefoksa linijkę (możemy to bezpiecznie zrobić, bo to tylko komentarz). Jeśli teraz powtórzymy nasze dotychczasowe kroki, zobaczymy, że wygenerowany XML jest wreszcie poprawny.&lt;/p&gt;&lt;h2 id="kolorujemy-polskę"&gt;Kolorujemy Polskę&lt;/h2&gt;&lt;p&gt;Widzieliśmy wcześniej, że główny element XML w naszym pliku zawiera 68 podelementów. Zobaczmy, jakie one są – wystarczą nam tagi:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; m))
(&lt;span class="hljs-symbol"&gt;:title&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:desc&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:defs&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:rect&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:g&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:g&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jak dotąd jest świetnie. Wygląda na to, że wszystkie opisy krajów są bezpośrednio zawarte w głównym elemencie jako podelementy. Spróbujmy znaleźć Polskę:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;count&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;filter&lt;/span&gt;&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;and&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;=&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt;)
                       (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;=&lt;/span&gt;&lt;/span&gt; ((&lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:id&lt;/span&gt;) &lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt;))
                 (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; m)))
&lt;span class="hljs-number"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(Ten kawałek kodu wybiera z listy podelementów &lt;code&gt;m&lt;/code&gt; takie, których tag to &lt;code&gt;path&lt;/code&gt;, a wartość atrybutu &lt;code&gt;id&lt;/code&gt; to &lt;code&gt;"pl"&lt;/code&gt; i zwraca długość takiej podlisty). Spróbujmy teraz dodać do tego elementu atrybut &lt;code&gt;style&lt;/code&gt;, zgodnie z tym, co powiedzieliśmy wcześniej. Ze względu na niezmienność struktur danych musimy zdefiniować nowy nadrzędny element, który będzie taki sam, jak &lt;code&gt;m&lt;/code&gt;, z tym, że odpowiedniemu podelementowi ustawimy styl:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;m2&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; m
                &lt;span class="hljs-symbol"&gt;:content&lt;/span&gt;
                (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;and&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;=&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt;)
                               (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;=&lt;/span&gt;&lt;/span&gt; ((&lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:id&lt;/span&gt;) &lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt;))
                        (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; % &lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; %) &lt;span class="hljs-symbol"&gt;:style&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;fill: #ff0000;&amp;quot;&lt;/span&gt;))
                        %)
                     (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; m))))
#&amp;#x27;user/m&lt;span class="hljs-number"&gt;2&lt;/span&gt;
&amp;gt; (&lt;span class="hljs-name"&gt;with-out-writer&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;/tmp/a.svg&amp;quot;&lt;/span&gt; (&lt;span class="hljs-name"&gt;emit&lt;/span&gt; m2))
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Otwieramy utworzony plik i widzimy mapkę z Polską zaznaczoną na czerwono. Hura!&lt;/p&gt;&lt;h2 id="generalizacja"&gt;Generalizacja&lt;/h2&gt;&lt;p&gt;Uogólnimy trochę nasz kod. Napiszemy funkcję, która pokoloruje jedno państwo, dostając na wejściu element &lt;code&gt;path&lt;/code&gt; (podelement &lt;code&gt;svg&lt;/code&gt;):&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;color-state&lt;/span&gt;
  [{&lt;span class="hljs-symbol"&gt;:keys&lt;/span&gt; [tag attrs] &lt;span class="hljs-symbol"&gt;:as&lt;/span&gt; element} colorize-fn]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [state (&lt;span class="hljs-symbol"&gt;:id&lt;/span&gt; attrs)]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if-let&lt;/span&gt;&lt;/span&gt; [color (&lt;span class="hljs-name"&gt;colorize-fn&lt;/span&gt; state)]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; element &lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; attrs &lt;span class="hljs-symbol"&gt;:style&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;str&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;fill:&amp;quot;&lt;/span&gt; color)))
      element)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jest to funkcja bardzo podobna do tej anonimowej, użytej powyżej w wywołaniu &lt;code&gt;map&lt;/code&gt;, ale różni się w kilku szczegółach. Po pierwsze, bierze dwa argumenty. Jednym jest, jak powiedzieliśmy, element XML-owy (od razu rozbity na &lt;code&gt;tag&lt;/code&gt; i &lt;code&gt;attrs&lt;/code&gt;: więcej o takiej notacji, zwanej &lt;em&gt;destructuring&lt;/em&gt; („dzielenie struktury”) można przeczytać w &lt;a href="http://clojure.org/special_forms"&gt;odpowiedniej części dokumentacji Clojure&lt;/a&gt;), a drugim... funkcja, która dostanie jako argument dwuliterowy kod państwa i zwróci HTML-owy opis jego koloru (lub &lt;code&gt;nil&lt;/code&gt;, jeśli kolor dla tego państwa nie jest określony – &lt;code&gt;color-state&lt;/code&gt; poradzi sobie z tym i zwróci element w stanie nienaruszonym).&lt;/p&gt;&lt;p&gt;Mając &lt;code&gt;color-state&lt;/code&gt;, możemy łatwo napisać funkcję wyższego poziomu, która w jednym kroku przetworzy i zapisze XML:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;save-color-map&lt;/span&gt;
  [svg colorize-fn outfile]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [colored-map (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; svg &lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; #(&lt;span class="hljs-name"&gt;color-state&lt;/span&gt; % colorize-fn) (&lt;span class="hljs-symbol"&gt;:content&lt;/span&gt; svg)))]
    (&lt;span class="hljs-name"&gt;with-out-writer&lt;/span&gt; out
      (&lt;span class="hljs-name"&gt;emit&lt;/span&gt; colored-map))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Testujemy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;save-color-map&lt;/span&gt; m {&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;#00ff00&amp;quot;&lt;/span&gt;} &lt;span class="hljs-string"&gt;&amp;quot;/tmp/a.svg&amp;quot;&lt;/span&gt;)
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tym razem Polska jest zielona (jako argumentu &lt;code&gt;color-state&lt;/code&gt; użyliśmy mapy kraj→kolor, bo mapy są w Clojure wołalne jak funkcje). Spróbujmy dorzucić niebieskie Niemcy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;save-color-map&lt;/span&gt; m {&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;#00ff00&amp;quot;&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;de&amp;quot;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;#0000ff&amp;quot;&lt;/span&gt;} &lt;span class="hljs-string"&gt;&amp;quot;/tmp/a.svg&amp;quot;&lt;/span&gt;)
&lt;span class="hljs-literal"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Działa!&lt;/p&gt;&lt;h2 id="problem-z-wielką-brytanią"&gt;Problem z Wielką Brytanią&lt;/h2&gt;&lt;p&gt;Zachęceni sukcesem, próbujemy kolorować różne kraje. Dla większości działa, ale Wielka Brytania jak była szara, tak jest, niezależnie od tego, czy podamy jej kod jako „uk”, czy jako „gb”. Zaglądamy jeszcze raz do źródła naszego obrazka. Komentarze na początku jak zwykle okazują się pomocne:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Certain countries are further subdivided the United Kingdom has gb-gbn for Great Britain and gb-nir for Northern Ireland. Russia is divided into ru-kgd for the Kaliningrad Oblast and ru-main for the Main body of Russia. There is the additional grouping #xb for the „British Islands” (the UK with its Crown Dependencies - Jersey, Guernsey and the Isle of Man)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Może więc trzeba podać „gb-gbn” i „gb-nir”, zamiast samego „gb”? Próbujemy i... też nie działa. Po chwili namysłu: no jasne! Nasze początkowe założenie, że &lt;em&gt;wszystkie&lt;/em&gt; definicje krajów są bezpośrednimi podelementami &lt;code&gt;path&lt;/code&gt;, okazuje się fałszywe. Trzeba to naprawić.&lt;/p&gt;&lt;p&gt;Dotychczas robiliśmy „płaską” transformację drzewa SVG: zamienialiśmy wszystkie podelementy elementu głównego i nic głębiej. Trzeba by zmienić wszystkie elementy &lt;code&gt;path&lt;/code&gt; (i &lt;code&gt;g&lt;/code&gt;, jeśli chcemy kolorować grupy takie jak Wielka Brytania), niezależnie od tego, jak głęboko siedzą w drzewie. Z pomocą przychodzi funkcja &lt;code&gt;map-zipper&lt;/code&gt; z &lt;a href="http://plblog.danieljanus.pl/zippery-w-clojure"&gt;poprzedniego odcinka&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Zważywszy, że funkcja &lt;code&gt;clojure.xml/zip-xml&lt;/code&gt; zwraca zipper działający na drzewach XML-owych, przepisujemy &lt;code&gt;save-color-map&lt;/code&gt; jako:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;save-color-map&lt;/span&gt;
  [svg colorize-fn outfile]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [colored-map (&lt;span class="hljs-name"&gt;map-zipper&lt;/span&gt; #(&lt;span class="hljs-name"&gt;color-state&lt;/span&gt; % colorize-fn) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [x] (#{&lt;span class="hljs-symbol"&gt;:g&lt;/span&gt; &lt;span class="hljs-symbol"&gt;:path&lt;/span&gt;} (&lt;span class="hljs-symbol"&gt;:tag&lt;/span&gt; x))) (&lt;span class="hljs-name"&gt;zip/xml-zip&lt;/span&gt; svg))]
    (&lt;span class="hljs-name"&gt;with-out-writer&lt;/span&gt; out
      (&lt;span class="hljs-name"&gt;emit&lt;/span&gt; colored-map))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tym razem Wielka Brytania już daje się pomalować.&lt;/p&gt;&lt;h2 id="koloryzatory"&gt;Koloryzatory&lt;/h2&gt;&lt;p&gt;Mamy już zautomatyzowany sam proces kolorowania, ale tłumaczenie konkretnych wartości liczbowych na RGB jest żmudne. W ostatniej części tego artykułu zobaczymy, jak je ułatwić: stworzymy funkcję zwracającą &lt;em&gt;koloryzator&lt;/em&gt;, czyli funkcję nadającą się do przekazania jako argument do &lt;code&gt;color-state&lt;/code&gt; i &lt;code&gt;save-color-map&lt;/code&gt; (dotąd używaliśmy w tym celu map).&lt;/p&gt;&lt;p&gt;Zaczniemy od napisania funkcji tłumaczącej trójkę liczb na HTML-owy zapis RGB, będzie nam bowiem łatwiej pracować z liczbami całkowitymi niż napisami:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;htmlize-color&lt;/span&gt;
  [[r g b]]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;format&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;#%02x%02x%02x&amp;quot;&lt;/span&gt; r g b))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wstawmy jeszcze wywołanie &lt;code&gt;htmlize-color&lt;/code&gt; w odpowiednim miejscu w &lt;code&gt;color-state&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;color-state&lt;/span&gt;
  [{&lt;span class="hljs-symbol"&gt;:keys&lt;/span&gt; [tag attrs] &lt;span class="hljs-symbol"&gt;:as&lt;/span&gt; element} colorize-fn]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [state (&lt;span class="hljs-symbol"&gt;:id&lt;/span&gt; attrs)]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if-let&lt;/span&gt;&lt;/span&gt; [color (&lt;span class="hljs-name"&gt;colorize-fn&lt;/span&gt; state)]
      (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; element &lt;span class="hljs-symbol"&gt;:attrs&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;assoc&lt;/span&gt;&lt;/span&gt; attrs &lt;span class="hljs-symbol"&gt;:style&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;str&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;fill:&amp;quot;&lt;/span&gt; (&lt;span class="hljs-name"&gt;htmlize-color&lt;/span&gt; color))))
      element)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wyobraźmy sobie teraz, że mamy tabelkę z wartościami liczbowymi dla państw, np. taką:&lt;/p&gt;&lt;table class="entry"&gt;
&lt;tr class="header"&gt;&lt;th&gt;Państwo&lt;/th&gt;&lt;th&gt;Wartość&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Polska&lt;/td&gt;&lt;td class="center"&gt;20&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Niemcy&lt;/td&gt;&lt;td class="center"&gt;15&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Holandia&lt;/td&gt;&lt;td class="center"&gt;30&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Chcemy mieć funkcję, która przypisze każdemu krajowi kolor tym intensywniejszy, im większa wartość, przy czym intensywność ma być proporcjonalna do wartości. Dla większej ogólności załóżmy, że mamy daną parę kolorów, c1 i c2, i dla każdej ze składowych R, G i B danemu państwu przypisujemy taką wartość tej składowej, jaka jest odległość rozważanej wartości od najmniejszej wartości w naszym zestawie danych (oczywiście znormalizowana biorąc pod uwagę długości odpowiednich przedziałów).&lt;/p&gt;&lt;p&gt;Brzmi to zagmatwanie, ale mam nadzieję, że na przykładzie będzie widać lepiej. Tak wygląda implementacja clojurowa powyższej funkcji:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;make-colorizer&lt;/span&gt;
  [dataset ranges]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [minv (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;apply&lt;/span&gt;&lt;/span&gt; min (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;vals&lt;/span&gt;&lt;/span&gt; dataset))
        maxv (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;apply&lt;/span&gt;&lt;/span&gt; max (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;vals&lt;/span&gt;&lt;/span&gt; dataset))
        progress (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [[min-col max-col]] (/ (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; max-col min-col) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; maxv minv))) ranges)]
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;into&lt;/span&gt;&lt;/span&gt; {}
          (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [[k v]] [(&lt;span class="hljs-name"&gt;.toLowerCase&lt;/span&gt; k) (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;map&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;fn&lt;/span&gt;&lt;/span&gt; [progress [min-color _]] (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;int&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;+&lt;/span&gt;&lt;/span&gt; min-color (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;*&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&lt;/span&gt;&lt;/span&gt; v minv) progress)))) progress ranges)])
               dataset))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Zobaczmy, jak działa na naszych przykładowych danych:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;make-colorizer&lt;/span&gt; {&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt; &lt;span class="hljs-number"&gt;20&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;de&amp;quot;&lt;/span&gt; &lt;span class="hljs-number"&gt;15&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;nl&amp;quot;&lt;/span&gt; &lt;span class="hljs-number"&gt;30&lt;/span&gt;} [[&lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;255&lt;/span&gt;] [&lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;] [&lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;]])
{&lt;span class="hljs-string"&gt;&amp;quot;pl&amp;quot;&lt;/span&gt; (&lt;span class="hljs-number"&gt;85&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;)&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;de&amp;quot;&lt;/span&gt; (&lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;)&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-string"&gt;&amp;quot;nl&amp;quot;&lt;/span&gt; (&lt;span class="hljs-number"&gt;255&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt;)}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Drugi argument oznacza, że składowa czerwona koloru ma się zmieniać od 0 do 255, a zielona i niebieska mają pozostać stałe (na poziomie 0).&lt;/p&gt;&lt;p&gt;Tak jak żądaliśmy, Niemcy wychodzą najciemniejsze (bo mają najmniejszą wartość), Holandia najjaśniejsza (bo ma największą wartość), a Polska jest w 1/3 jasności między Niemcami a Holandią (bo 20 jest w 1/3 drogi między 15 a 30).&lt;/p&gt;&lt;h2 id="podsumowanie"&gt;Podsumowanie&lt;/h2&gt;&lt;p&gt;Aplikację stworzoną w tym artykule można dalej rozwijać w wielu kierunkach. Można stworzyć jej wersję sieciową (jak się do tego zabrać -- opowiedziałem w skrócie na spotkaniu Warszawa JUG). Można pisać różne koloryzatory, np. dyskretny (stałe wartości dla różnych podprzedziałów wejściowego przedziału danych) albo „temperaturowy” (płynnie przechodzący od błękitu do czerwieni przez biel: trzeba w tym celu przejść przez przestrzeń kolorów HSV).&lt;/p&gt;&lt;p&gt;A jaki Wy macie pomysł na rozwinięcie przykładu? Zapraszam do komentowania i eksperymentowania! Dla tych, którym się nie chce przeklejać kodu do REPL, kompletne źródło działające z Leiningenem umieszczam na &lt;a href="https://github.com/nathell/color-europe"&gt;GitHub&lt;/a&gt;. Forki będą bardzo mile widziane.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2011-01-13:post:protokoly-i-git-bisect</id>
    <title>Pierwsze użycie: protokoły i git-bisect</title>
    <link href="http://plblog.danieljanus.pl/protokoly-i-git-bisect/"/>
    <updated>2011-01-13T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Lubię rozwiązywać problemy przy użyciu narzędzi, których dotychczas nie znałem albo znałem tylko teoretycznie, na zasadzie „wiem, że istnieje coś takiego i do czego z grubsza służy”. Jeszcze przyjemniej jest, kiedy uzyskane rozwiązanie okazuje się czytelniejsze, zrozumialsze, szybsze albo pod innymi względami lepsze niż wersja używająca tylko dotychczasowego „arsenału”. W tych dniach zdarzyło mi się tego doświadczyć dwukrotnie, gdy pracowałem nad &lt;a href="http://fablo.pl"&gt;Fablo&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Fablo to napisany w &lt;a href="http://clojure.org"&gt;Clojure&lt;/a&gt; silnik wyszukiwarki dla polskich sklepów internetowych: zna odmianę polskich wyrazów, obsługuje literówki i błędy ortograficzne. Radzi sobie też z wyrazami wpisanymi bez polskich znaków, a więc wie, że „papryka zolta” to to samo, co „papryka żółta”. Działa to na ogół zupełnie dobrze, ale wciąż ulepszamy wyszukiwarkę i eliminujemy błędy polegające na tym, że na jakieś zapytanie nie wyszukują się niektóre produkty, choć powinny. Tym razem błąd brzmiał: zapytanie „sledzie” zwraca inne wyniki niż „śledzie”.&lt;/p&gt;&lt;p&gt;Analiza problemu ujawniła, że algorytm odpowiedzialny za „polszczenie” zapytania powinien być stosowany szerzej. Mamy dwie implementacje pewnej struktury danych używanej w tym algorytmie: jedna z nich jest czysto funkcyjna i zbudowana z rdzennie clojurowego tworzywa – map i wektorów; druga zaś jest ukryta za fasadą używanej przez nas biblioteki Javowej o prostym interfejsie, a więc explicite do tej pory z niej nie korzystaliśmy. Algorytm polszczenia był uruchamiany tylko na pierwszej implementacji (co wystarczy w wielu przypadkach, ale nie zawsze), a powinien być na obu.&lt;/p&gt;&lt;p&gt;Koncepcyjnie te dwie implementacje są podobne, ale mają zupełnie różne API – zrazu wydawało mi się więc, że będę musiał pisać drugą wersję funkcji realizującej „polszczenie”. To jednak nie byłoby optymalne: duplikacja kodu oznacza dwa razy więcej okazji do popełnienia błędu i konieczność pamiętania o wprowadzeniu zmian każdorazowo w obu miejscach. Z pomocą przyszły &lt;a href="http://clojure.org/Protocols"&gt;protokoły&lt;/a&gt; – nowy element języka wprowadzony w Clojure 1.2.&lt;/p&gt;&lt;p&gt;Protokół jest czymś bardzo zbliżonym do znanego z Javy interfejsu: to zestaw deklaracji nazwanych funkcji i ich argumentów, jednak bez implementacji. Implementacje definiuje się dla konkretnych typów. Mogą to być zarówno typy definiowane w Clojure (przy użyciu konstrukcji &lt;code&gt;deftype&lt;/code&gt;), jak i istniejące klasy Javowe. Można więc zadeklarować w protokole jakąś operację (nazwijmy ją blabalizacją), po czym zdefiniować, co oznacza blabalizowanie liczb, a co napisów. Efekt jest taki, jak gdybyśmy w Javie jakoś zmusili klasy &lt;code&gt;java.lang.Integer&lt;/code&gt; i &lt;code&gt;java.lang.String&lt;/code&gt; do implementowania interfejsu &lt;code&gt;Blabalize&lt;/code&gt;; możemy wołać odpowiednią funkcję bezpośrednio na obiektach odpowiednich klas, bez żadnych wrapperów!&lt;/p&gt;&lt;p&gt;Zacząłem więc od zdefiniowania protokołu, opisującego dwie operacje, jakich można dokonywać na mojej strukturze danych. Potem przerobiłem implementację algorytmu „polszczącego” zapytania tak, aby korzystała tylko z tych dwóch operacji, co uniezależniło ją od implementacji „pod spodem”. Napisanie Clojurowej implementacji protokołu było proste; trudniejsze okazało się odpowiednie skorzystanie z biblioteki Javowej, ale i to udało się zrobić w miarę szybko.&lt;/p&gt;&lt;p&gt;Efekt: brak duplikacji kodu i tylko nieznacznie zmodyfikowana dotychczasowa wersja. A przy tym udało się nagiąć bibliotekę Javową do robienia rzeczy, do których nie była zaprojektowana, bez ingerencji w jej kod. Clojure nie przestaje mnie zadziwiać.&lt;/p&gt;&lt;p&gt;Innym razem zauważyłem, że od kilku commitów przy próbie uruchomienia testów jednostkowych i regresji w logach testowania pojawia się komunikat, którego zdecydowanie nie powinno tam być. Mógłbym zabrać się do tego testując okolice pojawienia się komunikatu w REPL, ale tym razem postanowiłem zrobić to inaczej. Ponieważ używamy &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt; do kontroli wersji, wykorzystałem narzędzie &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html"&gt;git-bisect&lt;/a&gt;, aby znaleźć commit wprowadzający komunikat. Potrzebuje ono do działania trzech rzeczy: identyfikatorów commitu „dobrego” i „złego” oraz skryptu, który odpowiada na pytanie, czy dany commit jest „dobry” czy „zły” (zwracającego kod wyjścia odpowiednio 0 albo 1). Skrypt wyglądał mniej więcej następująco:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs bash"&gt;cake clean
cake deps
cake proto
&lt;span class="hljs-built_in"&gt;sleep&lt;/span&gt; 10
cake release
&lt;span class="hljs-built_in"&gt;rm&lt;/span&gt; *.&lt;span class="hljs-built_in"&gt;log&lt;/span&gt;
make &lt;span class="hljs-built_in"&gt;test&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;if&lt;/span&gt; grep -q &lt;span class="hljs-string"&gt;&amp;quot;PODEJRZANY_KOMUNIKAT&amp;quot;&lt;/span&gt; fablo*&lt;span class="hljs-built_in"&gt;log&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;then&lt;/span&gt;
  &lt;span class="hljs-built_in"&gt;exit&lt;/span&gt; 1
&lt;span class="hljs-keyword"&gt;else&lt;/span&gt;
  &lt;span class="hljs-built_in"&gt;exit&lt;/span&gt; 0
&lt;span class="hljs-keyword"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(&lt;a href="http://github.com/ninjudd/cake"&gt;Cake&lt;/a&gt; jest narzędziem, którego używamy do budowania oprogramowania; 10-sekundowa przerwa między &lt;code&gt;cake proto&lt;/code&gt; a &lt;code&gt;cake release&lt;/code&gt; jest remedium na błąd w cake.)&lt;/p&gt;&lt;p&gt;Potem wystarczyło tylko:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs bash"&gt;$ git bisect start HEAD dobry_commit --
$ git bisect run check.sh
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i już wiedziałem, w którym miejscu się zepsuło, a stąd było już niedaleko do znalezienia przyczyny problemu.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2010-11-25:post:zippery-w-clojure</id>
    <title>Zippery w Clojure</title>
    <link href="http://plblog.danieljanus.pl/zippery-w-clojure/"/>
    <updated>2010-11-25T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;Niniejszy artykuł jest pierwszym z dwuczęściowego minicyklu, stanowiącego demonstrację wykorzystania Clojure w praktyce do prostego, acz nietrywialnego zadania, na które natknąłem się w codziennej pracy. Dzisiejsza część może wydać się mało interesująca i mocno teoretyczna, ale mam nadzieję, że następny odcinek pokaże, jak można ją ciekawie wykorzystać (na razie nie zdradzę, jakie to wykorzystanie).&lt;/p&gt;&lt;p&gt;Planuję, że takie artykuły lub cykle zdominują Clojurową część tego bloga – będę się tu dzielił rozwiązaniami praktycznych problemów, na jakie natrafiam. Nie będzie tu artykułów w stylu „hej, jaki fajny nowy framework XYZ, napiszmy w nim Hello World przy wykorzystaniu technologii ABC!”, chyba że XYZ lub ABC będą przydatnymi narzędziami do rozwiązania problemu z życia wziętego. Bo takie rozwiązywanie w Clojure daje dużo radości: nie przypadkiem taki właśnie jest tytuł &lt;a href="http://joyofclojure.com/"&gt;książki M. Fogusa i C. Housera&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;A dziś opowiem o ciekawej strukturze danych, jaką są &lt;em&gt;zippery&lt;/em&gt;. (Mierzi mnie trochę używanie angielskiej nazwy, ale nie potrafię wymyślić dobrego polskiego odpowiednika tego słowa; będę szczęśliwy widząc propozycje w komentarzach!) Cóż to takiego?&lt;/p&gt;&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Zipper_(data_structure)"&gt;Zipper&lt;/a&gt; jest strukturą danych, dającą &lt;em&gt;iluzję imperatywności&lt;/em&gt; przy manipulowaniu drzewami. Pamiętamy, że Clojurowe natywne struktury danych są niezmienne (&lt;em&gt;immutable&lt;/em&gt;): jeśli mamy listę składającą się z liczb 2 i 5, to nie możemy jej w żaden sposób zmienić (np. dodać elementu) -- możemy co najwyżej stworzyć na jej podstawie &lt;em&gt;nową&lt;/em&gt; listę, która będzie zawierać wszystkie elementy listy wyjściowej i jeszcze jakiś.&lt;/p&gt;&lt;p&gt;Niezmienność jest bardzo przydatna (o filozoficznych podstawach takiego podejścia, które zadecydowały o jego wykorzystaniu w Clojure, można poczytać w artykule &lt;a href="http://clojure.org/state"&gt;„On State and Identity”&lt;/a&gt;), ale wymusza myślenie w innych kategoriach. To jest to, co czasami nazywa się „myśleniem funkcyjnym”: zamiast zastanawiać się, w jaki sposób &lt;em&gt;zmienić&lt;/em&gt; wartość naszej zmiennej, aby doprowadzić ją do pożądanego stanu, pytamy o to, &lt;em&gt;jak z jednych wartości robić inne&lt;/em&gt;. Przy tym nigdzie nie jest powiedziane, że taki sposób myślenia jest koniecznie lepszy od imperatywnego, do którego przyzwyczajeni są programiści języków takich jak Java. Jest po prostu inny. Warto się go nauczyć, bo okazuje się, że wiele problemów się upraszcza, gdy już umysł się przestawi na taki &lt;em&gt;modus operandi&lt;/em&gt;. Bywa jednak i tak, że o danym problemie imperatywnie myśli się wygodniej niż funkcyjnie. Tu właśnie wkraczają zippery.&lt;/p&gt;&lt;img src="/img/blog/zipper.png"&gt;
&lt;p&gt;Wyobraźmy sobie, że chcemy dodać liczbę 4 do konkretnego drzewa BST, tak jak na rysunku powyżej. Załóżmy chwilowo, że nie interesuje nas ogólna funkcja dodająca do BST, a tylko chcemy wstawić czwórkę w konkretne miejsce. Jak to zrobić imperatywnie?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Zejdź dwa razy w dół w prawo („dobierz się” do węzła 6).&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wstaw czwórkę na lewo od bieżącego węzła.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I już. A teraz funkcyjnie – pamiętamy, że z drzewa robimy nowe drzewo:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Nowym drzewem jest drzewo, którego korzeniem jest korzeń drzewa wyjściowego, lewym poddrzewem – lewe poddrzewo drzewa wyjściowego, a prawym poddrzewem drzewo, którego korzeniem jest korzeń prawego poddrzewa drzewa wyjściowego, lewym poddrzewem – drzewo składające się z tylko jednego węzła 4, a prawym poddrzewem – prawe poddrzewo prawego poddrzewa drzewa wyjściowego.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Pierwsze podejście jest łatwiejsze, prawda? Zipper pozwala nam zachować je prawie niezmienione, nie rezygnując przy tym z zalet niezmienności. Oto jak można by opisać dodawanie do drzewa z zipperem:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Stwórz zipper na podstawie drzewa wyjściowego.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „zejdź w dół”, otrzymując nowy zipper.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „przejdź w prawo”, otrzymując nowy zipper.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „zejdź w dół”, otrzymując nowy zipper.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „wstaw 4 przed bieżącym elementem”, otrzymując nowy zipper.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Wykonaj na tym zipperze operację „daj drzewo wynikowe”, otrzymując pożądane drzewo.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Clojure zawiera implementację zipperów w bibliotece standardowej (w przestrzeni nazw &lt;code&gt;clojure.zip&lt;/code&gt;). Oto jak można by zapisać powyższy przykład w Clojure:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;nowe-drzewo&lt;/span&gt;
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;let&lt;/span&gt;&lt;/span&gt; [z1 (&lt;span class="hljs-name"&gt;zip&lt;/span&gt; drzewo)
        z2 (&lt;span class="hljs-name"&gt;down&lt;/span&gt; z1)
        z3 (&lt;span class="hljs-name"&gt;right&lt;/span&gt; z2)
        z4 (&lt;span class="hljs-name"&gt;down&lt;/span&gt; z3)
        z5 (&lt;span class="hljs-name"&gt;insert-left&lt;/span&gt; z4 &lt;span class="hljs-number"&gt;4&lt;/span&gt;)]
    (&lt;span class="hljs-name"&gt;root&lt;/span&gt; z5)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(tu &lt;em&gt;explicite&lt;/em&gt; nazywam kolejne kroki obliczeń). Albo tak, używając makra &lt;code&gt;-&gt;&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;nowe-drzewo&lt;/span&gt;
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&amp;gt;&lt;/span&gt;&lt;/span&gt; drzewo
      zip
      down
      right
      down
      (&lt;span class="hljs-name"&gt;insert-left&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;)
      root)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Proste i wygodne. Pod spodem zipper to po prostu oryginalne drzewo plus informacja o tym, w którym miejscu drzewa w tej chwili jesteśmy, plus lista „zmian”, jakie do tej pory zostały na nim wykonane.&lt;/p&gt;&lt;p&gt;Rozwiązując mój problem (na razie nie zdradzam, jaki), natknąłem się na potrzebę posiadania funkcji, która działałaby jak &lt;code&gt;map&lt;/code&gt;, ale na drzewach, a nie listach. To znaczy, przekształcałaby każdy element, niezależnie od tego, jak głęboko w drzewie siedzi, aplikując do niego jakąś funkcję, i zwracała w wyniku nowe drzewo. Co więcej, chciałem móc dodatkowo kontrolować, które węzły są zmieniane: funkcja ma dostawać dodatkowy predykat i tylko kiedy zwróci on &lt;code&gt;true&lt;/code&gt; na wartości danego węzła, zmieniać tę wartość. Nie ma chyba takiej funkcji w standardowej bibliotece, ale dzięki zipperom można ją łatwo napisać.&lt;/p&gt;&lt;p&gt;Tak się szczęśliwie składa, że Clojurowe zippery mają funkcję &lt;code&gt;next&lt;/code&gt;, która spaceruje po drzewie „w głąb”. Mając je, mogłem pomyśleć tak o swojej implementacji:&lt;/p&gt;&lt;p&gt;Jeśli zipper jest na końcu drzewa, to wynikiem transformacji jest wynikowe drzewo dla tego zippera, w przeciwnym razie wynikiem jest ta sama transformacja wywołana rekurencyjnie dla zippera uzyskanego przez wykonanie operacji „edit” (edycji bieżącego węzła w razie potrzeby) i „next”.&lt;/p&gt;&lt;p&gt;Tłumacząc to na Clojure, dostajemy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;map-zipper&lt;/span&gt; [f pred z]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;zip/end?&lt;/span&gt; z)
    (&lt;span class="hljs-name"&gt;zip/root&lt;/span&gt; z)
    (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;recur&lt;/span&gt;&lt;/span&gt; f pred (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&amp;gt;&lt;/span&gt;&lt;/span&gt; z (&lt;span class="hljs-name"&gt;zip/edit&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;pred&lt;/span&gt; %) (&lt;span class="hljs-name"&gt;f&lt;/span&gt; %) %)) zip/next)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I testujemy:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;map-zipper&lt;/span&gt; inc integer?
              (&lt;span class="hljs-name"&gt;zip/vector-zip&lt;/span&gt; [&lt;span class="hljs-number"&gt;1&lt;/span&gt; [&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;5&lt;/span&gt;] [&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]]))
[&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; [&lt;span class="hljs-number"&gt;4&lt;/span&gt; &lt;span class="hljs-number"&gt;5&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;6&lt;/span&gt;] [&lt;span class="hljs-number"&gt;7&lt;/span&gt; [[&lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt; &lt;span class="hljs-number"&gt;10&lt;/span&gt;]]]]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Trzeba podać predykat &lt;code&gt;integer?&lt;/code&gt;, bo zipper działający na zagnieżdżonych wektorach działa na poszczególnych poddrzewach, a nie tylko na liściach (które tu są liczbami). Możemy łatwo zobaczyć, jakie poddrzewa odwiedza &lt;code&gt;map-zipper&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;&amp;gt; (&lt;span class="hljs-name"&gt;map-zipper&lt;/span&gt; #(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;do&lt;/span&gt;&lt;/span&gt; (&lt;span class="hljs-name"&gt;println&lt;/span&gt; %) %)
              (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;constantly&lt;/span&gt;&lt;/span&gt; &lt;span class="hljs-literal"&gt;true&lt;/span&gt;)
              (&lt;span class="hljs-name"&gt;zip/vector-zip&lt;/span&gt; [&lt;span class="hljs-number"&gt;1&lt;/span&gt; [&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;5&lt;/span&gt;] [&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]]))
[&lt;span class="hljs-number"&gt;1&lt;/span&gt; [&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;5&lt;/span&gt;] [&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]]
&lt;span class="hljs-number"&gt;1&lt;/span&gt;
[&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]]
&lt;span class="hljs-number"&gt;2&lt;/span&gt;
[&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]
&lt;span class="hljs-number"&gt;3&lt;/span&gt;
&lt;span class="hljs-number"&gt;4&lt;/span&gt;
[&lt;span class="hljs-number"&gt;5&lt;/span&gt;]
&lt;span class="hljs-number"&gt;5&lt;/span&gt;
[&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]
&lt;span class="hljs-number"&gt;6&lt;/span&gt;
[[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]
[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]
&lt;span class="hljs-number"&gt;7&lt;/span&gt;
&lt;span class="hljs-number"&gt;8&lt;/span&gt;
&lt;span class="hljs-number"&gt;9&lt;/span&gt;
[&lt;span class="hljs-number"&gt;1&lt;/span&gt; [&lt;span class="hljs-number"&gt;2&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-number"&gt;4&lt;/span&gt;]] [&lt;span class="hljs-number"&gt;5&lt;/span&gt;] [&lt;span class="hljs-number"&gt;6&lt;/span&gt; [[&lt;span class="hljs-number"&gt;7&lt;/span&gt; &lt;span class="hljs-number"&gt;8&lt;/span&gt; &lt;span class="hljs-number"&gt;9&lt;/span&gt;]]]]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <id>tag:plblog.danieljanus.pl,2010-11-11:post:leniwa-wersja-makra</id>
    <title>Leniwa wersja makra -&gt;</title>
    <link href="http://plblog.danieljanus.pl/leniwa-wersja-makra/"/>
    <updated>2010-11-11T00:00:00Z</updated>
    <content type="html">&lt;div&gt;&lt;p&gt;&lt;a href="http://jaceklaskowski.pl"&gt;Jacek Laskowski&lt;/a&gt; podaje ciekawy &lt;a href="http://blog.japila.pl/2010/11/monady-maybe-odsona-kolejna-rozwiazanie.html"&gt;przykład wykorzystania monad w Clojure&lt;/a&gt; -- aplikowanie kolejnych funkcji do wyrażenia, dopóki ma ono wartość nie będącą &lt;code&gt;nil&lt;/code&gt;.  Przykład mi się podoba, bo jest prosty, ale nie trywialny – wykorzystuje monadę &lt;code&gt;maybe&lt;/code&gt; do eleganckiego rozwiązania rzeczywistego problemu.  Jest to w dodatku problem, z którym borykają się czasem programiści piszący w Javie, co widać choćby w &lt;a href="http://groups.google.com/group/pl.comp.lang.java/browse_thread/thread/d290ced113686b34/f4cc877a5053969d"&gt;tym wątku&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Nie mogę powiedzieć, że rozumiem monady. Owszem, znam definicję i potrafię napisać program w Haskellu wykorzystujący monadę &lt;code&gt;IO&lt;/code&gt;, ale każdy monadyczny kod, jaki dotychczas widziałem, dawał się zapisać równie zwięźle bez użycia monad w językach, które mniej religijnie podchodzą do niewpływania funkcji na stan świata zewnętrznego. Tak jest również w tym przypadku.&lt;/p&gt;&lt;p&gt;Jacek tak postawił oryginalny problem:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Napisać metodę, która zwraca walutę, dla pracownika z danego departamentu międzynarodowej korporacji. Pracownik jest przypisany do departamentu (np. poprzez mapę – pracownik-departament), departament do kraju, a kraj do waluty. Funkcja na wejściu dostaje nazwę, identyfikator, lub cokolwiek jednoznacznie reprezentującego pracownika, a na wyjściu symbol waluty, np. dla „Jacek” powinno być „PLN”, a dla „John” „USD”, a „Tomek” i „Mateusz” dawaliby „CHF”.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Moje pierwotne rozwiązanie jest po prostu złożeniem trzech funkcji:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defn&lt;/span&gt; &lt;span class="hljs-title"&gt;pracownik-&amp;gt;waluta&lt;/span&gt; [p]
  (&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;-&amp;gt;&lt;/span&gt;&lt;/span&gt; p pracownik-&amp;gt;departament departament-&amp;gt;kraj kraj-&amp;gt;waluta))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;O ile wszystkie z nich są mapami (pamiętamy, że w Clojure mapy implementują interfejs &lt;code&gt;IFn&lt;/code&gt;, można je więc uważać za funkcje i wołać jak funkcje), to dla żadnej wartości wejściowej nie zostanie rzucony &lt;code&gt;NullPointerException&lt;/code&gt;, ponieważ wywołanie mapy dla nieistniejącego klucza zwraca &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Jacek słusznie zauważa jednak, że wykonujemy w ten sposób więcej pracy niż trzeba. Jeżeli już w mapie &lt;code&gt;pracownik-&gt;departament&lt;/code&gt; nie ma departamentu dla danego pracownika, to otrzymany &lt;code&gt;nil&lt;/code&gt; zostanie „przepchnięty” przez pozostałe dwie mapy, zanim będzie zwrócony. Można kontrargumentować, że taka implementacja &lt;code&gt;pracownik-&gt;waluta&lt;/code&gt; jest bardzo czytelna i mała strata wydajności jest niewielką ceną do zapłacenia za tę czytelność.  Co jednak, gdy nie możemy lub nie chcemy zgodzić się na taką stratę, a jednocześnie nie chcemy stracić czytelności?&lt;/p&gt;&lt;p&gt;Odpowiedzią jest makro, które nazwałem &lt;code&gt;and-&gt;&lt;/code&gt;. Działa ono tak samo, jak &lt;code&gt;-&gt;&lt;/code&gt;, z tą różnicą, że kolejne wartości oblicza tylko jeśli po drodze nie pojawiło się &lt;code&gt;false&lt;/code&gt; lub &lt;code&gt;nil&lt;/code&gt;, podobnie jak &lt;code&gt;and&lt;/code&gt; (stąd nazwa). Oto ono:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs clojure"&gt;(&lt;span class="hljs-keyword"&gt;defmacro&lt;/span&gt; &lt;span class="hljs-title"&gt;and-&amp;gt;&lt;/span&gt;
  ([x] x)
  ([x form] `(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;when-let&lt;/span&gt;&lt;/span&gt; [y# ~x] (~form y#)))
  ([x form &amp;amp; more] `(&lt;span class="hljs-name"&gt;&lt;span class="hljs-built_in"&gt;when-let&lt;/span&gt;&lt;/span&gt; [y# (&lt;span class="hljs-name"&gt;and-&amp;gt;&lt;/span&gt; ~x ~form)]
                      (&lt;span class="hljs-name"&gt;and-&amp;gt;&lt;/span&gt; y# ~@more))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Implementacja jest bardzo zbliżona do zwykłego &lt;code&gt;-&gt;&lt;/code&gt;, którego kod w Clojure 1.2 wygląda &lt;a href="https://github.com/clojure/clojure/blob/1.2.0/src/clj/clojure/core.clj#L1284"&gt;tak&lt;/a&gt;. W stosunku do &lt;code&gt;-&gt;&lt;/code&gt; moje makro jest dla większej poglądowości trochę uproszczone i nie obsługuje konstrukcji typu &lt;code&gt;(and-&gt; mapa (get :klucz))&lt;/code&gt;, które normalnie rozwijane są do &lt;code&gt;(get mapa :klucz)&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Warto zwrócić uwagę na to, że argument makra nigdy nie jest wyliczany w jego rozwinięciu więcej niż raz; zawsze jest wiązany do lokalnego symbolu o unikatowej nazwie za pomocą &lt;code&gt;when-let&lt;/code&gt;. Jest to jedna z reguł pozwalających unikać błędów w makrach. Szerszy opis tego problemu, występującego też w Common Lispie, można znaleźć w &lt;a href="http://gigamonkeys.com/book/macros-defining-your-own.html#plugging-the-leaks"&gt;rozdziale 8 książki „Practical Common Lisp”&lt;/a&gt;.&lt;/p&gt;&lt;h2 id="aktualizacja-(7.12.2010)"&gt;Aktualizacja (7.12.2010)&lt;/h2&gt;&lt;p&gt;Jak się okazuje, clojure-contrib &lt;a href="http://clojuredocs.org/clojure_contrib/clojure.contrib.core/-_q%3E"&gt;zawiera już&lt;/a&gt; takie makro! Nazywa się &lt;code&gt;-?&gt;&lt;/code&gt; i można je znaleźć w przestrzeni nazw &lt;code&gt;clojure.contrib.core&lt;/code&gt;. To już któryś raz, kiedy okazuje się, że jakieś użyteczne makro lub funkcja już jest w contrib i nie trzeba jej było pisać.&lt;/p&gt;&lt;/div&gt;</content>
  </entry>
</feed>
