Tytuł oryginału: Programming Clojure
Tłumaczenie: Tomasz Walczak
ISBN: 978-83-246-5372-0
© Helion 2013.
All rights reserved.
Copyright © 2012 The Pragmatic Programmers, LLC.
All rights reserved.
No part of this publication may be reproduced, stored in retrieval system, or transmitted,
in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise,
without the prior consent of the poublisher.
Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej
publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną,
fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym
powoduje naruszenie praw autorskich niniejszej publikacji.
Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi
ich właścicieli.
Wydawnictwo HELION dołożyło wszelkich starań, by zawarte w tej książce informacje
były kompletne i rzetelne. Nie bierze jednak żadnej odpowiedzialności ani za ich wykorzystanie,
ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Wydawnictwo
HELION nie ponosi również żadnej odpowiedzialności za ewentualne szkody wynikłe
z wykorzystania informacji zawartych w książce.
Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)
Pliki z przykładami omawianymi w książce można znaleźć pod adresem:
ftp://ftp.helion.pl/przyklady/proclo.zip
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/proclo
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Printed in Poland.
Spis treci
Podzikowania ............................................................................................10
Przedmowa do wydania drugiego .............................................................11
Przedmowa do wydania pierwszego ........................................................13
Wstp ...........................................................................................................15
Rozdzia 1. Wprowadzenie .........................................................................23
1.1. Dlaczego Clojure? .............................................................................................24
1.2. Szybkie wprowadzenie do programowania w Clojure ...........................................34
1.3. Biblioteki jzyka Clojure .....................................................................................40
1.4. Podsumowanie ..................................................................................................44
Rozdzia 2. Przegld jzyka Clojure ..........................................................45
2.1. Konstrukcje skadniowe ......................................................................................46
2.2. Makra odczytu ...................................................................................................55
2.3. Funkcje .............................................................................................................56
2.4. Zmienne, wizania i przestrzenie nazw ...............................................................61
2.5. Wywoywanie kodu Javy .....................................................................................68
2.6. Przepyw sterowania ..........................................................................................70
2.7. Gdzie si podziaa ptla for? ..............................................................................74
2.8. Metadane ..........................................................................................................77
2.9. Podsumowanie ..................................................................................................79
Kup książkę
Poleć książkę
8
Programowanie w jzyku Clojure
Rozdzia 3. Ujednolicanie danych za pomoc sekwencji .......................81
3.1. Wszystko jest sekwencj .....................................................................................83
3.2. Stosowanie biblioteki sekwencji ...........................................................................87
3.3. Sekwencje nieskoczone i „leniwe” .....................................................................96
3.4. W Clojure Java jest sekwencyjna .........................................................................98
3.5. Funkcje przeznaczone dla konkretnych struktur ..................................................104
3.6. Podsumowanie ................................................................................................113
Rozdzia 4. Programowanie funkcyjne ...................................................115
4.1. Zagadnienia z obszaru programowania funkcyjnego ...........................................116
4.2. Jak stosowa „leniwe” podejcie? ......................................................................121
4.3. Leniwsze ni leniwe .........................................................................................130
4.4. Jeszcze o rekurencji ..........................................................................................136
4.5. Podsumowanie ................................................................................................146
Rozdzia 5. Stan .........................................................................................147
5.1. Wspóbieno, równolego i blokady .............................................................148
5.2. Referencje i pami STM ................................................................................150
5.3. Nieskoordynowane i synchroniczne aktualizacje za pomoc atomów ....................157
5.4. Stosowanie agentów do asynchronicznego aktualizowania danych .......................158
5.5. Zarzdzanie stanem specyficznym dla wtku za pomoc zmiennych ....................163
5.6. Gra Snake w jzyku Clojure .............................................................................168
5.7. Podsumowanie ................................................................................................178
Rozdzia 6. Protokoy i typy danych .......................................................179
6.1. Programowanie z wykorzystaniem abstrakcji ......................................................180
6.2. Interfejsy .........................................................................................................183
6.3. Protokoy ........................................................................................................184
6.4. Typy danych ...................................................................................................188
6.5. Rekordy ..........................................................................................................193
6.6. Makro reify .....................................................................................................198
6.7. Podsumowanie ................................................................................................199
Kup książkę
Poleć książkę
Spis treci
9
Rozdzia 7. Makra .....................................................................................201
7.1. Kiedy naley stosowa makra? ..........................................................................202
7.2. Makro do sterowania przebiegiem programu ......................................................202
7.3. Upraszczanie makr ..........................................................................................209
7.4. Taksonomia makr ............................................................................................214
7.5. Podsumowanie ................................................................................................224
Rozdzia 8. Wielometody ..........................................................................225
8.1. ycie bez wielometod ......................................................................................226
8.2. Definiowanie wielometod .................................................................................228
8.3. Wicej ni proste wybieranie metod ...................................................................231
8.4. Tworzenie doranych taksonomii ......................................................................233
8.5. Kiedy naley korzysta z wielometod? ...............................................................237
8.6. Podsumowanie ................................................................................................241
Rozdzia 9. Sztuczki z Jav ......................................................................243
9.1. Obsuga wyjtków ............................................................................................244
9.2. Zmagania z liczbami cakowitymi .....................................................................248
9.3. Optymalizowanie wydajnoci ............................................................................250
9.4. Tworzenie klas Javy w jzyku Clojure ................................................................255
9.5. Praktyczny przykad .........................................................................................261
9.6. Podsumowanie ................................................................................................268
Rozdzia 10. Tworzenie aplikacji ............................................................269
10.1. Wynik w grze Clojurebreaker ..........................................................................270
10.2. Testowanie kodu zwracajcego wynik ..............................................................274
10.3. Biblioteka test.generative ................................................................................278
10.4. Tworzenie interfejsu .......................................................................................287
10.5. Instalowanie kodu ..........................................................................................292
10.6. Poegnanie ....................................................................................................295
Dodatek A. Edytory kodu .........................................................................297
Dodatek B. Bibliografia ............................................................................299
Skorowidz ..................................................................................................301
Kup książkę
Poleć książkę
22
Programowanie w jzyku Clojure
Kup książkę
Poleć książkę
Rozdzia 1.
Wprowadzenie
zybki wzrost popularnoci jzyka Clojure wynika z wielu przyczyn. Po krót-
kich poszukiwaniach w sieci WWW dowiesz si, e Clojure:
jest jzykiem funkcyjnym;
jest Lispem na maszyny JVM;
ma specjalne mechanizmy do obsugi wspóbienoci.
Wszystkie te cechy s wane, jednak adna z nich nie odgrywa najwaniejszej
roli. Naszym zdaniem najistotniejszymi aspektami jzyka Clojure s jego pro-
stota i moliwoci.
Prostota w kontekcie oprogramowania jest wana z kilku wzgldów, tu jednak
mamy na myli pierwotne i najwaniejsze znaczenie tego sowa — proste jest
to, co nie jest zoone. Proste komponenty umoliwiaj systemowi przeprowa-
dzanie operacji zaplanowanych przez projektantów i nie wykonuj czynnoci
niepowizanych z danym zadaniem. Z naszych dowiadcze wynika, e nie-
wielka zoono zwykle szybko przeksztaca si w niebezpiecznie powan.
Take sowo moliwoci ma wiele znacze. Tu mamy na myli zdolno do
wykonywania stawianych aplikacji zada. Aby programista mia odpowiednie
moliwoci, musi wykorzysta platform, która sama je posiada i jest powszechnie
dostpna. Tak platform jest na przykad maszyna JVM. Ponadto uywane
narzdzia musz zapewnia peny, nieograniczony dostp do oferowanych
moliwoci. Dostp do moliwoci jest czsto podstawowym wymogiem w pro-
jektach, w których trzeba w peni wykorzysta platform.
S
Kup książkę
Poleć książkę
24
Programowanie w jzyku Clojure
Przez lata tolerowalimy bardzo skomplikowane narzdzia, które byy jedynym
sposobem na uzyskanie potrzebnych moliwoci. Czasem akceptowalimy te ogra-
niczone moliwoci w celu uproszczenia modelu programowania. Niekiedy nie da
si unikn pewnych kompromisów, jednak w obszarze moliwoci i prostoty nie
trzeba si z nimi godzi. Jzyk Clojure to dowód na to, e cechy te mona poczy.
1.1. Dlaczego Clojure?
Wszystkie charakterystyczne cechy jzyka Clojure maj zapewnia prostot,
moliwoci lub i jedno, i drugie. Oto kilka przykadów:
Programowanie funkcyjne jest proste, poniewa pozwala oddzieli ob-
liczenia od stanu i tosamoci. Zalet jest to, e programy funkcyjne s
atwiejsze do zrozumienia, pisania, testowania, optymalizowania i rów-
nolegego wykonywania.
Konstrukcje umoliwiajce wspódziaanie jzyków Clojure i Java daj
due moliwoci, poniewa zapewniaj bezporedni dostp do skadni
Javy. Zalet jest to, e mona uzyska wydajno na poziomie Javy
i stosowa skadni charakterystyczn dla tego jzyka. Co waniejsze,
nie trzeba ucieka si do jzyka niszego poziomu, aby zapewni sobie
dodatkowe moliwoci.
Lisp jest prosty w dwóch bardzo wanych aspektach — oddziela
wczytywanie od wykonania, a jego skadnia obejmuje niewielk liczb
niezalenych elementów. Zalet jest to, e wzorce projektowe s ujte
w abstrakcyjne konstrukcje skadniowe, a S-wyraenia obejmuj kod
w jzykach XML, JSON i SQL.
Lisp daje te due moliwoci, poniewa udostpnia kompilator i sys-
tem makr dziaajcy w czasie wykonywania programu. Zalet jest to, e
w Lispie wystpuje póne wizanie i mona atwo tworzy jzyki DSL.
Model czasu w jzyku Clojure jest prosty. Oddzielono w nim wartoci,
tosamo, stan i czas. Zalet jest to, e w programach mona spraw-
dza i zapamitywa informacje bez obaw o to, e starsze dane zostan
nadpisane.
Protokoy s proste. W Clojure polimorfizm jest niezaleny od dzie-
dziczenia. Zalet jest to, e mona bezpiecznie i w konkretnym celu
rozszerza typy oraz abstrakcje. Nie wymaga to stosowania zawiych
wzorców projektowych lub wprowadzania zmian w cudzym kodzie
(co czsto prowadzi do bdów).
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
25
Ta lista cech stanowi „map” pomocn w dalszych rozdziaach ksiki. Nie
martw si, jeli na razie nie rozumiesz wszystkich szczegóów. Kadej z tych
cech powicamy cay rozdzia.
Zobaczmy, jak niektóre z tych cech sprawdzaj si w praktyce. Zbudujmy
w tym celu prost aplikacj. Przy okazji dowiesz si, jak wczytywa i wykony-
wa wiksze przykadowe programy, które prezentujemy dalej w ksice.
Jzyk Clojure jest elegancki
W jzyku Clojure stosunek sygnau do szumu jest wysoki. Dlatego programy
pisane w tym jzyku s krótkie. Takie programy s tasze w tworzeniu, insta-
lowaniu i konserwacji
1
. Jest to prawd zwaszcza wtedy, gdy programy s zwi-
ze, a nie tylko treciwe. Przyjrzyj si na przykad poniszemu kodowi w Javie
(pochodzi on z projektu Apache Commons):
data/snippets/isBlank.java
public class StringUtils {
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}
}
Metoda
isBlank()
sprawdza, czy acuch znaków jest pusty (nie zawiera ad-
nych znaków lub obejmuje same odstpy). Oto kod podobnej metody w jzyku
Clojure:
src/examples/introduction.clj
(defn blank? [str]
(every? #(Character/isWhitespace %) str))
Wersja w jzyku Clojure jest krótsza, a co waniejsze — prostsza. Nie wyst-
puj tu zmienne, modyfikowalny stan ani rozgazienia. Efekt ten jest moliwy
dziki funkcjom wyszego rzdu. Funkcje tego rodzaju przyjmuj inne funkcje
1
Software Estimation: Demystifying the Black Art [McC06] to znakomita ksika.
Jej autorzy dowodz, e krótsze jest tasze.
Kup książkę
Poleć książkę
26
Programowanie w jzyku Clojure
jako argumenty i (lub) zwracaj funkcje. Funkcja
every?
przyjmuje funkcj
i kolekcj, a zwraca warto
true
, jeli otrzymana funkcja zwraca
true
dla ka-
dego elementu z kolekcji.
Poniewa w wersji w jzyku Clojure nie ma rozgazie, kod jest bardziej czy-
telny i atwiejszy do przetestowania. Zalety te s jeszcze wyraniejsze w wik-
szych programach. Ponadto, cho kod jest zwizy, mona go atwo zrozumie.
Program w jzyku Clojure mona potraktowa jak definicj pustego acucha
znaków — jest to acuch, w którym kady znak jest odstpem. Kod ten jest
znacznie lepszy od metody z projektu Commons, w którym definicja pustego
acucha znaków jest ukryta za szczegóowym kodem ptli i instrukcji
if
.
Oto inny przykad. Przyjrzyj si banalnej klasie
Person
napisanej w jzyku Java:
data/snippets/Person.java
public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
W jzyku Clojure rekord
Person
mona zdefiniowa w jednym wierszu:
(defrecord Person [first-name last-name])
Korzysta z tego rekordu mona tak:
(def foo (->Person "Aaron" "Bedra"))
-> #'user/foo
foo
-> #:user.Person{:first-name "Aaron", :last-name "Bedra"}
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
27
Instrukcj
defrecord
i powizane funkcje omawiamy w podrozdziale 6.3,
„Protokoy”.
Kod w jzyku Clojure nie tylko jest znacznie krótszy; rekord
Person
jest tu nie-
zmienny. Niezmienne struktury danych s z natury bezpieczne ze wzgldu
na wtki, a mechanizmy modyfikowania danych mona utworzy w odrbnej
warstwie za pomoc referencji, agentów i atomów jzyka Clojure. Techniki te
omawiamy w rozdziale 5., „Stan”. Poniewa rekordy s niezmienne, jzyk
Clojure automatycznie udostpnia poprawne implementacje funkcji
hashCode()
i
equals()
.
Jzyk Clojure ma wbudowanych wiele eleganckich rozwiza, jeli jednak
stwierdzisz, e czego Ci w nim brakuje, moesz samodzielnie doda potrzebne
mechanizmy. Umoliwiaj to cechy Lispa.
Clojure to odwieony Lisp
Clojure jest Lispem. Od dziesicioleci zwolennicy Lispa mówi o przewagach,
jakie jzyk ten ma w porównaniu z waciwie wszystkimi innymi jzykami. Plan
opanowania wiata przez Lispa jest jednak realizowany do powoli.
Twórcy jzyka Clojure, podobnie jak autorzy kadej odmiany Lispa, musieli
zmierzy si z dwoma problemami. Oto one:
Clojure ma odnie sukces jako odmiana Lispa. Wymaga to przeko-
nania uytkowników Lispa, e Clojure obejmuje najwaniejsze mecha-
nizmy swojego poprzednika.
Jednoczenie Clojure ma odnie sukces tam, gdzie wczeniejsze wersje
Lispa si nie przyjy. Wymaga to zdobycia poparcia wikszej spoecz-
noci programistów.
Autorzy jzyka Clojure radz sobie z tymi problemami przez udostpnienie me-
chanizmów metaprogramowania (charakterystycznych dla Lispa) i wprowadze-
nie zestawu usprawnie skadniowych uatwiajcych stosowanie jzyka Clojure
programistom, którzy nie znaj Lispa.
Dlaczego Lisp?
Wersje Lispa maj may rdze, s prawie pozbawione skadni i maj rozbudo-
wane mechanizmy do obsugi makr. Z uwagi na te cechy mona zmodyfikowa
Lispa pod ktem projektu, zamiast dostosowywa projekt do Lispa. Przyjrzyj si
poniszemu fragmentowi kodu w Javie:
Kup książkę
Poleć książkę
28
Programowanie w jzyku Clojure
public class Person {
private String firstName;
public String getFirstName() {
// Cig dalszy.
W kodzie wystpuje metoda
getFirstName()
. Metody s polimorficzne i mona je
dostosowa do potrzeb. Jednak znaczenie kadego innego sowa w przykado-
wym kodzie jest okrelane przez jzyk. Czasem wygodna jest moliwo zmiany
znaczenia poszczególnych sów. Pozwala to na przykad:
zdefiniowa sowo
private
jako „prywatne w kodzie produkcyjnym, ale
publiczne na potrzeby serializacji i testów jednostkowych”;
zdefiniowa sowo
class
w taki sposób, aby platforma automatycznie
generowaa metody pobierajce i ustawiajce dla pól prywatnych (o ile
programista nie zarzdzi inaczej);
utworzy podklas dla
class
i umieci w niej wywoywane zwrotnie
uchwyty dla zdarze cyklu ycia; klasa z obsug cyklu ycia moe na
przykad zgasza zdarzenie utworzenia egzemplarza tej klasy.
Natykalimy si na programy, w których potrzebne byy wszystkie te cechy. Bez
potrzebnych mechanizmów programici musieli ucieka si do powtarzalnych
i podatnych na bdy sztuczek. Aby poradzi sobie z brakiem niezbdnych
rozwiza, napisano dosownie miliony wierszy kodu.
W wikszoci jzyków trzeba poprosi osoby odpowiedzialne za implementacj,
aby doday wspomniane wczeniej mechanizmy. W Clojure moesz samodzielnie
doda potrzebne cechy, piszc makra (rozdzia 7., „Makra”). Nawet sam jzyk
Clojure jest zbudowany za pomoc makr, takich jak
defrecord
:
(defrecord name [arg1 arg2 arg3])
Jeli chcesz zmieni znaczenie sowa, moesz napisa wasne makro. Jeeli
potrzebujesz rekordów o cisej kontroli typów i z opcjonalnym sprawdzaniem,
czy pola nie maj wartoci
null
, wystarczy utworzy wasne makro
defrecord
.
Powinno ono wyglda tak:
(defrecord name [Type :arg1 Type :arg2 Type :arg3]
:allow-nulls false)
Moliwo zmiany dziaania jzyka w nim samym jest wyjtkow zalet Lispa.
Mona znale wiele opisów rónych aspektów tego podejcia:
Lisp jest homoikoniczny
2
. Oznacza to, e kod w Lispie to dane. Dlatego
atwo jest tworzy programy za pomoc innych programów.
2
http://pl.wikipedia.org/wiki/homoikoniczno
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
29
Cay jzyk jest dostpny w kadym momencie. Paul Graham w artykule
Revenge of the Nerds
3
wyjania, dlaczego jest to wane.
Skadnia Lispa eliminuje te problemy z priorytetami operatorów i cznoci
operacji. W ksice tej nie znajdziesz tabel dotyczcych tych zagadnie. Z uwagi
na wymóg stosowania nawiasów wyraenia s jednoznaczne.
Wad prostej, jednolitej skadni Lispa (przynajmniej dla pocztkujcych) jest
nacisk na nawiasy i listy (listy to gówny typ danych w Lispie). Clojure udo-
stpnia ciekawe poczenie cech, które uatwiaj uywanie Lispa programistom
znajcym inne jzyki.
Lisp z mniejsz liczb nawiasów
Clojure zapewnia istotne zalety programistom uywajcym innych odmian Lispa.
Oto te korzyci:
W jzyku Clojure fizyczne listy z Lispa s uogólnione do abstrakcyjnej
postaci — sekwencji. Pozwala to zachowa moliwoci, jakie daj listy,
a przy tym wykorzysta ich zalety w innych strukturach danych.
Wykorzystanie maszyn JVM jako podstawy dla kodu w jzyku Clojure
daje dostp do standardowej biblioteki i bardzo popularnej platformy.
Sposób analizowania symboli i cudzysowów sprawia, e w jzyku
Clojure pisanie standardowych makr jest stosunkowo proste.
Wielu uytkowników jzyka Clojure nie zna Lispa, za to prawdopodobnie
syszao niepochlebne opinie na temat stosowania w nim nawiasów. W jzyku
Clojure zachowano nawiasy (i moliwoci Lispa!), jednak pod kilkoma wzgl-
dami usprawniono tradycyjn skadni Lispa.
Clojure zapewnia wygodn, opart na literaach skadni dla rónych
struktur danych (nie tylko dla list), takich jak wyraenia regularne, od-
wzorowania, zbiory, wektory i metadane. Dlatego kod w jzyku Clojure
jest mniej zaleny od list ni w wikszoci innych odmian Lispa. Midzy
innymi parametry funkcji s podawane w wektorach,
[]
, a nie w listach,
()
.
src/examples/introduction.clj
(defn hello-world [username]
(println (format "Witaj, %s" username)))
Zastosowanie wektora sprawia, e lista argumentów jest lepiej widoczna,
co uatwia czytanie definicji funkcji w jzyku Clojure.
3
http://www.paulgraham.com/icad.html
Kup książkę
Poleć książkę
30
Programowanie w jzyku Clojure
W jzyku Clojure, inaczej ni w wikszoci odmian Lispa, przecinki
s traktowane jak odstpy.
; Wektory przypominaj tablice z innych jzyków.
[1, 2, 3, 4]
-> [1 2 3 4]
Jzyk Clojure jest idiomatyczny i nie wymaga niepotrzebnego zagnie-
dania nawiasów. Przyjrzyj si makru
cond
, które wystpuje zarówno
w Common Lispie, jak i w Clojure. Makro
cond
sprawdza zestaw par
test-wynik i zwraca pierwszy wynik, dla którego warto wyraenia te-
stowego to
true
. Kada para test-wynik znajduje si w nawiasach:
; Makro cond w Common Lispie.
(cond ((= x 10) "equal")
((> x 10) "more"))
W jzyku Clojure dodatkowe nawiasy s niepotrzebne.
; Makro cond w Clojure.
(cond (= x 10) "equal"
(> x 10) "more")
Jest to kwestia czysto estetyczna i oba podejcia maj swoich zwolenników.
Wane jest to, e jzyk Clojure pozbawiono uciliwych aspektów Lispa,
jeli byo to moliwe bez utraty zalet tego ostatniego.
Clojure jest wietn odmian Lispa zarówno dla ekspertów, jak i dla poczt-
kujcych programistów Lispa.
Clojure to jzyk funkcyjny
Clojure jest jzykiem funkcyjnym, natomiast nie jest (w odrónieniu od Haskella)
czysto funkcyjny. Oto cechy jzyków funkcyjnych:
Funkcje to penoprawne obiekty. Oznacza to, e funkcje mona two-
rzy w czasie wykonywania programu, przekazywa, zwraca i ogólnie
uywa ich jak wszelkich innych typów danych.
Dane s niezmienne.
Funkcje s czyste, czyli nie maj efektów ubocznych.
W wielu obszarach programy funkcyjne s atwiejsze do zrozumienia, mniej
podatne na bdy i znacznie atwiejsze do wielokrotnego uytku. Na przykad
poniszy krótki program wyszukuje w bazie danych utwory kadego kompozy-
tora, który napisa dzieo pod tytuem „Requiem”:
(for [c compositions :when (= "Requiem" (:name c))] (:composer c))
-> ("W. A. Mozart" "Giuseppe Verdi")
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
31
Sowo
for
nie jest pocztkiem ptli, ale wyraenia listowego (ang. list compre-
hension). Kod ten oznacza „dla kadego
c
z kolekcji
compositions
, gdzie nazw
c
jest
"Requiem"
, podaj kompozytora
c
”. Wyraenia listowe omawiamy w punkcie
„Przeksztacanie sekwencji”.
Przykadowy kod ma cztery podane cechy:
Jest prosty. Nie obejmuje ptli, zmiennych ani zmiennego stanu.
Jest bezpieczny ze wzgldu na wtki. Nie wymaga stosowania blokad.
Jest moliwy do równolegego wykonywania. Kady krok mona przy-
dzieli do odrbnego wtku bez koniecznoci modyfikowania kodu po-
szczególnych kroków.
Jest uniwersalny. Kolekcj
compositions
moe by zwyky zbiór, kod
w XML-u lub zbiór wyników z bazy danych.
Warto porówna programy funkcyjne z programami imperatywnymi, w których
instrukcje zmieniaj stan programu. Wikszo programów obiektowych pisze si
w stylu imperatywnym, dlatego nie maj one adnych z wymienionych wczeniej
zalet. S niepotrzebnie skomplikowane, niebezpieczne ze wzgldu na wtki, nie
umoliwiaj równolegego dziaania, a kod jest trudny do uogólnienia. Bezpo-
rednie porównanie podejcia funkcyjnego i imperatywnego znajdziesz w pod-
rozdziale 2.7, „Gdzie si podziaa ptla for?”.
Programici znaj zalety jzyków funkcyjnych ju od dugiego czasu. Jednak
jzyki funkcyjne, na przykad Haskell, nie zdobyy dominujcej pozycji. Wyni-
ka to z tego, e nie wszystkie operacje mona wygodnie wykona w podejciu
czysto funkcyjnym.
S cztery powody, dla których jzyk Clojure moe zyska wiksze zaintereso-
wanie ni dawne jzyki funkcyjne:
Podejcie funkcyjne jest dzi bardziej przydatne ni kiedykolwiek
wczeniej. Pojawiaj si maszyny o coraz wikszej liczbie rdzeni, a j-
zyki funkcyjne pozwalaj atwo wykorzysta moliwoci takiego sprztu.
Programowanie funkcyjne omawiamy w rozdziale 4., „Programowanie
funkcyjne”.
W jzykach funkcyjnych niewygodnie zarzdza si stanem, jeli musi
si on zmienia. Clojure udostpnia mechanizmy do obsugi zmien-
nego stanu za pomoc programowej pamici transakcyjnej i referencji,
agentów, atomów i wizania dynamicznego.
Kup książkę
Poleć książkę
32
Programowanie w jzyku Clojure
W wielu jzykach funkcyjnych typy s okrelane statycznie. W jzyku
Clojure stosuje si dynamiczne okrelanie typów, co uatwia zadanie
programistom poznajcym dopiero programowanie funkcyjne.
Wywoywanie w jzyku Clojure kodu Javy nie odbywa si w modelu
funkcyjnym. Przy korzystaniu z Javy wkraczamy w znany wiat zmien-
nych obiektów. Zapewnia to wygod pocztkujcym, którzy ucz si
programowania funkcyjnego, a take pozwala w razie potrzeby zrezy-
gnowa z podejcia funkcyjnego. Wywoywanie kodu Javy opisujemy
w rozdziale 9., „Sztuczki z Jav”.
Mechanizmy zarzdzania zmian stanu w jzyku Clojure umoliwiaj pisanie
programów wspóbienych bez bezporedniego korzystania z blokad i uzupe-
niaj podstawowy funkcjonalny rdze jzyka.
Clojure upraszcza programowanie wspóbiene
Mechanizmy programowania funkcyjnego dostpne w Clojure uatwiaj pisanie
kodu bezpiecznego ze wzgldu na wtki. Poniewa niezmienne struktury danych
nigdy si nie zmieniaj, nie wystpuje zagroenie uszkodzeniem danych w wy-
niku dziaania innych wtków.
Jednak obsuga wspóbienoci w Clojure wykracza poza mechanizmy progra-
mowania funkcyjnego. Jeli potrzebne s referencje do zmiennych danych, Clojure
zabezpiecza je za pomoc programowej pamici transakcyjnej (ang. software
transactional memory — STM). STM suy do tworzenia kodu bezpiecznego
ze wzgldu na wtki i jest rozwizaniem wyszego poziomu ni blokady z Javy.
Zamiast stosowa podatne na bdy strategie blokowania danych, mona za-
bezpieczy wspóuytkowany stan za pomoc transakcji. Jest to duo lepsze
podejcie, poniewa wielu programistów dobrze zna transakcje z uwagi na do-
wiadczenie w korzystaniu z baz danych.
Poniszy kod tworzy dziaajc, bezpieczn ze wzgldu na wtki baz danych
z kontami przechowywan w pamici:
(def accounts (ref #{}))
(defrecord Account [id balance])
Funkcja
ref
tworzy zabezpieczon za pomoc transakcji referencj do biecego
stanu bazy danych. Aktualizowanie stanu jest niezwykle proste. Poniej poka-
zujemy, jak doda do bazy nowe konto:
(dosync
(alter accounts conj (->Account "CLJ" 1000.00)))
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
33
Instrukcja
dosync
powoduje aktualizowanie bazy
accounts
w transakcji. Rozwi-
zanie to zapewnia bezpieczestwo ze wzgldu na wtki i jest atwiejsze w sto-
sowaniu od blokad. Dziki transakcjom nigdy nie trzeba martwi si o to, które
obiekty i w jakiej kolejnoci naley zablokowa. Podejcie transakcyjne spraw-
dza si lepiej take w niektórych standardowych zastosowaniach. Przykadowo:
w modelu tym wtki wczytujce dane nigdy nie musz blokowa danych.
Cho przedstawiony przykad jest bardzo prosty, technika jest ogólna i spraw-
dza si take w praktyce. Wicej informacji o wspóbienoci i pamici STM
w jzyku Clojure znajdziesz w rozdziale 5., „Stan”.
Wykorzystanie maszyny JVM w Clojure
Clojure zapewnia przejrzysty, prosty i bezporedni dostp do Javy. W kodzie
mona bezporednio wywoa metody z dowolnego interfejsu API Javy:
(System/getProperties)
-> {java.runtime.name=Java(TM) SE Runtime Environment
... i wiele innych ...
Clojure obejmuje wiele skadniowych mechanizmów do wywoywania kodu
w Javie. Nie omawiamy tu ich szczegóowo (zobacz podrozdzia 2.5, „Wywo-
ywanie kodu w Javie”), warto jednak wspomnie, e w Clojure wystpuje mniej
kropek i mniej nawiasów ni w analogicznym kodzie Javy:
// Java
"hello".getClass().getProtectionDomain()
; Clojure
(.. "hello" getClass getProtectionDomain)
Clojure udostpnia proste funkcje do implementowania interfejsów Javy i two-
rzenia klas pochodnych od klas Javy. Ponadto wszystkie funkcje jzyka Clojure
obejmuj implementacj interfejsów
Callable
i
Runnable
. Dlatego mona atwo
przekaza ponisz funkcj anonimow do konstruktora klasy
Thread
Javy:
(.start (new Thread (fn [] (println "Witaj" (Thread/currentThread)))))
-> Witaj #<Thread Thread[Thread-0,5,main]>
Dziwne dane wyjciowe wynikaj ze sposobu wywietlania informacji o obiek-
tach Javy w jzyku Clojure.
Thread
to nazwa klasy danego obiektu, a czon
Thread[Thread-0,5,main]
to efekt wywoania metody
toString
obiektu.
(Zauwa, e w przedstawionym kodzie nowy wtek dziaa a do zakoczenia pracy,
natomiast jego dane wyjciowe mog w dziwny sposób przeplata si z wierszami
zachty rodowiska REPL. Nie jest to problem z jzykiem Clojure, a jedynie
wynik zapisywania danych do strumienia wyjcia przez wicej ni jeden wtek).
Kup książkę
Poleć książkę
34
Programowanie w jzyku Clojure
Poniewa skadnia do wywoywania kodu Javy w Clojure jest przejrzysta i pro-
sta, zwykle uywa si Javy bezporednio, zamiast ukrywa kod w tym jzyku za
charakterystycznymi dla Lispa nakadkami.
Poznae ju kilka powodów do stosowania jzyka Clojure. Pora przystpi do
pisania kodu.
1.2. Szybkie wprowadzenie
do programowania w Clojure
Aby uruchomi rodowisko jzyka Clojure i kod z tej ksiki, potrzebujesz
dwóch rzeczy. Oto one:
rodowisko uruchomieniowe Javy. Pobierz
4
i zainstaluj Jav w wersji
5. lub nowszej. W wersji 6. znacznie poprawiono wydajno i system
informowania o wyjtkach, dlatego warto stosowa wanie j.
Leiningen
5
. Leiningen to narzdzie do zarzdzania zalenociami i uru-
chamiania operacji na kodzie. Jest to take najpopularniejsze w wiecie
jzyka Clojure narzdzie do wykonywania tych zada.
Leiningen posuy do zainstalowania jzyka Clojure i wszystkich elementów
wymaganych w przykadowym kodzie. Jeli masz ju zainstalowane narzdzie
Leiningen, wiesz zapewne, jak z niego korzysta. Jeeli jeszcze nie masz po-
trzebnej wiedzy, zapoznaj si z krótkim wprowadzeniem ze strony narzdzia
w serwisie GitHub
6
. Znajdziesz tam instrukcje dotyczce instalacji, a take pod-
stawowe informacje na temat uytkowania Leiningena. Nie musisz jednak uczy
si wszystkiego, poniewa w ksice opisujemy polecenia potrzebne do uru-
chomienia przykadów.
W trakcie pracy z ksik korzystaj z jzyka Clojure w wersji waciwej dla
przykadowego kodu. Po przeczytaniu ksiki moesz zastosowa si do in-
strukcji z ramki „Samodzielne budowanie jzyka Clojure” i zbudowa aktualn
wersj jzyka.
4
http://www.oracle.com/technetwork/java/javase/downloads/index.html
5
http://github.com/technomancy/leiningen
6
http://github.com/technomancy/leiningen
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
35
W punkcie „Pobieranie przykadowego kodu”, znajdziesz instrukcje dotyczce
pobierania przykadowego kodu. Po cigniciu przykadowego kodu trzeba
uy Leiningena do pobrania zalenoci. W katalogu gównym z kodem wywoaj
nastpujc instrukcj:
lein deps
Samodzielne budowanie jzyka Clojure
Moliwe, e chcesz zbudowa jzyk Clojure na podstawie kodu ródo-
wego, aby uzyska nowe funkcje i wprowadzi poprawki bdów. Mona
zrobi to w nastpujcy sposób:
git clone git://github.com/clojure/clojure.git
cd clojure
mvn package
Przykadowy kod jest regularnie aktualizowany pod ktem nowych rozwi-
za wprowadzanych w jzyku Clojure. Zapoznaj si z plikiem README
w przykadowym kodzie, aby sprawdzi numer najnowszej wersji, dla
której sprawdzono kod.
Zalenoci s pobierane i umieszczane w odpowiednim miejscu. Moesz prze-
testowa zainstalowane narzdzia przez przejcie do katalogu z przykadowym
kodem i uruchomienie rodowiska REPL jzyka Clojure. Leiningen obejmuje
skrypt uruchomieniowy rodowiska REPL, który wczytuje jzyk Clojure wraz
z zalenociami potrzebnymi w dalszych rozdziaach.
lein repl
Po udanym uruchomieniu rodowiska REPL powinien pojawi si wiersz
zachty z tekstem
user=>
:
Clojure
user=>
Teraz jeste gotowy do wywietlenia tekstu „Witaj, wiecie”.
Korzystanie ze rodowiska REPL
Aby pokaza, jak korzysta ze rodowiska REPL, tworzymy kilka wersji kodu
wywietlajcego tekst „Witaj, wiecie”. Najpierw wpisz kod
(println "Witaj,
wiecie")
w wierszu zachty rodowiska REPL.
user=> (println "Witaj, wiecie")
-> Witaj, wiecie
Kup książkę
Poleć książkę
36
Programowanie w jzyku Clojure
Drugi wiersz,
Witaj, wiecie
, to dane dane wyjciowe z konsoli.
Teraz umiemy kod w funkcji, która potrafi „zwraca si” do uytkownika po
imieniu.
(defn hello [name] (str "Witaj, " name))
-> #'user/hello
Rozómy ten kod na fragmenty. Oto one:
defn
suy do definiowania funkcji;
hello
to nazwa funkcji;
funkcja
hello
przyjmuje jeden argument,
name
;
str
to wywoanie funkcji czcej dowoln list argumentów w acuch
znaków;
defn
,
hello
,
name
i
str
to symbole, czyli nazwy prowadzce do rónych
elementów; dozwolone symbole opisujemy w punkcie „Symbole”.
Przyjrzyj si zwracanej wartoci,
#'user/hello
. Przedrostek
#'
oznacza, e
funkcj zapisano w zmiennej jzyka Clojure, a
user
to przestrze nazw, w której
znajduje si ta funkcja. Jest to domylna przestrze nazw w rodowisku REPL,
odpowiadajca domylnemu pakietowi w Javie. Na razie zmienne i przestrzenie
nazw nie maj znaczenia. Omawiamy je w podrozdziale 2.4, „Zmienne, wi-
zanie i przestrzenie nazw”.
Teraz mona wywoa funkcj
hello
i przekaza do niej imi.
user=> (hello "Janku")
-> "Witaj, Janku"
Jeli rodowisko REPL znajduje si w dziwnym stanie, najatwiej zamkn je
za pomoc kombinacji klawiszy Ctrl+C w systemie Windows lub Ctrl+D
w systemach uniksowych, a nastpnie ponownie uruchomi.
Specjalne zmienne
rodowisko REPL obejmuje szereg przydatnych zmiennych specjalnych. W czasie
pracy w rodowisku REPL wyniki obliczania trzech ostatnich wyrae znaj-
duj si w specjalnych zmiennych
*1
,
*2
i
*3
. Pozwala to na wygodn prac
w modelu iteracyjnym. Spróbujmy poczy kilka powita.
user=> (hello "Janku")
-> "Witaj, Janku"
user=> (hello "Clojure")
-> "Witaj, Clojure"
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
37
Teraz mona zastosowa specjalne zmienne do poczenia wyników ostatnich
instrukcji.
(str *1 " i " *2)
-> "Witaj, Clojure i Witaj, Janku"
Popenienie bdu w rodowisku REPL prowadzi do zgoszenia wyjtku Javy
(z uwagi na zwizo szczegóy pomijamy). Niedozwolone jest na przykad
dzielenie przez zero.
user=> (/ 1 0)
-> ArithmeticException Divide by zero clojure.lang.Numbers.divide
Tu problem jest oczywisty, jednak czasem jest bardziej skomplikowany i po-
trzebujemy szczegóowego stosu wywoa. W specjalnej zmiennej
*e
znajduj
si informacje o ostatnim wyjtku. Poniewa wyjtki w Clojure s wyjtkami
Javy, mona wywietli stos wywoa za pomoc instrukcji
pst
(od ang. print
stacktrace, czyli wywietl stos wywoa)
7
.
user=> (pst)
-> ArithmeticException Divide by zero
| clojure.lang.Numbers.divide
| sun.reflect.NativeMethodAccessorImpl.invoke0
| sun.reflect.NativeMethodAccessorImpl.invoke
| sun.reflect.DelegatingMethodAccessorImpl.invoke
| java.lang.reflect.Method.invoke
| clojure.lang.Reflector.invokeMatchingMethod
| clojure.lang.Reflector.invokeStaticMethod
| user/eval1677
| clojure.lang.Compiler.eval
| clojure.lang.Compiler.eval
| clojure.core/eval
Wspódziaanie z Jav omawiamy w rozdziale 9., „Sztuczki z Jav”.
Jeli blok kodu jest zbyt dugi, aby mona go wygodnie wpisa w rodowisku
REPL, umie kod w pliku, a nastpnie wczytaj ten plik w rodowisku. Moesz
uy cieki bezwzgldnej lub poda j wzgldem miejsca uruchomienia rodo-
wiska REPL.
; Zapisz kod w pliku temp.clj, a nastpnie wywoaj instrukcj:
user=> (load-file "temp.clj")
REPL to znakomite rodowisko do wypróbowywania pomysów i otrzymywania
natychmiastowych informacji zwrotnych. Aby jak najlepiej wykorzysta ksik,
w trakcie jej lektury nie zamykaj rodowiska REPL.
7
Instrukcja
pst
jest dostpna tylko w Clojure 1.3.0 i nowszych wersjach.
Kup książkę
Poleć książkę
38
Programowanie w jzyku Clojure
Dodawanie stanu wspóuytkowanego
Funkcja
hello
z poprzedniego przykadu to czysta funkcja, czyli taka, której
dziaanie nie ma efektów ubocznych. Czyste funkcje atwo si pisze i testuje.
S take atwe do zrozumienia, dlatego w wielu sytuacjach warto je stosowa.
Jednak w wikszoci programów wystpuje wspóuytkowany stan, a do zarz-
dzania nim su funkcje typu impure (czyli takie, które nie s czyste). Dodajmy
do funkcji
hello
mechanizm ledzenia liczby uytkowników. Najpierw trzeba
utworzy struktur danych do przechowywania tej liczby. Uyjmy do tego zbioru.
#{}
-> #{}
#{}
to litera oznaczajcy pusty zbiór. Potrzebna jest te operacja
conj
.
(conj coll item)
Instrukcja
conj
(od ang. conjoin, czyli czy) tworzy now kolekcj z dodawanym
elementem. Doczmy element do zbioru, aby upewni si, e powstaje nowy zbiór.
(conj #{} "Janku")
-> #{"Janku"}
Tworzenie nowych zbiorów jest ju moliwe. Pora opracowa sposób na spraw-
dzanie aktualnego zbioru uytkowników. Clojure udostpnia kilka sucych do
tego typów referencyjnych. Najprostszym typem referencyjnym jest atom.
(atom initial-state)
Aby okreli nazw atomu, naley uy instrukcji
def
.
(def symbol initial-value?)
Instrukcja
def
przypomina polecenie
defn
, ale jest ogólniejsza. Przy jej uyciu
mona definiowa funkcje lub dane. Uyjmy sowa
atom
do utworzenia atomu
i instrukcji
def
do powizania atomu z nazw
visitors
.
(def visitors (atom #{}))
-> #'user/visitors
Aby zaktualizowa referencj, trzeba uy funkcji, na przykad
swap!
.
(swap! r update-fn & args)
Funkcja
swap!
przeprowadza operacj
update-fn
na referencji
r
i w razie potrzeby
uywa przy tym opcjonalnych argumentów
args
. Spróbujmy wstawi uytkow-
nika do kolekcji
visitors
, uywajc do aktualizowania funkcji
conj
.
(swap! visitors conj "Janku")
-> #{"Janku"}
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
39
atom
to tylko jeden z kilku typów referencyjnych dostpnych w jzyku Clojure.
Wybór odpowiedniego typu referencyjnego nie jest prosty (zagadnienie to
omawiamy w rozdziale 5., „Stan”).
W dowolnym momencie mona sprawdzi zawarto pamici, do której prowadzi
referencja. Suy do tego instrukcja
deref
lub jej krótszy odpowiednik,
@
.
(deref visitors)
-> #{"Janku"}
@visitors
-> #{"Janku"}
Teraz mona zbudowa now, bardziej rozbudowan wersj funkcji
hello
.
src/examples/introduction.clj
(defn hello
"Wywietla powitanie na wyjciu, uywajc nazwy uytkownika.
Potrafi stwierdzi, e korzystae ju z programu."
[username]
(swap! visitors conj username)
(str "Witaj, " username))
Teraz sprawdmy, czy uytkownicy s poprawnie zapisywani w pamici.
(hello "Marku")
-> "Witaj, Marku"
@visitors
-> #{"Jacku" "Janku" "Marku"}
Na Twoim komputerze lista uytkowników bdzie prawdopodobnie inna. Na tym
wanie polega problem ze stanem. Efekty s róne w zalenoci od tego, kiedy
zaszy dane zdarzenia. Zrozumie funkcj mona na podstawie jej analizy.
Aby zrozumie stan, trzeba pozna ca histori dziaania programu.
Jeli to moliwe, unikaj przechowywania stanu. Jeeli jest to niemoliwe, dbaj
o to, aby stanem mona byo zarzdza. Uywaj do tego typów referencyjnych,
na przykad atomów. Atomy (i wszystkie inne typy referencyjne w Clojure) s
bezpieczne przy korzystaniu z wielu wtków i procesorów. Co lepsze, zapew-
nienie bezpieczestwa nie wymaga stosowania blokad, które bywaj skompli-
kowane w uyciu.
Na tym etapie powiniene umie ju wprowadza krótkie fragmenty kodu
w rodowisku REPL. Wprowadzanie wikszych porcji kodu odbywa si podob-
nie. Take biblioteki jzyka Clojure mona wczytywa i uruchamia z poziomu
rodowiska REPL. Dalej pokazujemy, jak to zrobi.
Kup książkę
Poleć książkę
40
Programowanie w jzyku Clojure
1.3. Biblioteki jzyka Clojure
Kod jzyka Clojure jest umieszczony w bibliotekach. Kada biblioteka jzyka
Clojure znajduje si w przestrzeni nazw, która jest odpowiednikiem pakietu
Javy. Bibliotek jzyka Clojure mona wczyta za pomoc instrukcji
require
.
(require quoted-namespace-symbol)
Jeli programista da biblioteki o nazwie
clojure.java.io
, Clojure szuka pliku
o nazwie clojure/java/io.clj w ciece ze zmiennej
CLASSPATH
. Zobaczmy, jaki jest
tego efekt.
user=> (require 'clojure.java.io)
-> nil
Pocztkowy pojedynczy apostrof (
'
) jest niezbdny i suy do dosownego po-
dawania (ang. quoting) nazwy biblioteki (podawanie nazw omawiamy w pod-
rozdziale 2.2, „Makra odczytu”). Zwrócona warto
nil
oznacza powodzenie.
Przy okazji sprawd, czy moesz wczyta przykadowy kod do tego rozdziau
(bibliotek
examples.introduction
).
user=> (require 'examples.introduction)
-> nil
Biblioteka
examples.introduction
obejmuje implementacj generowania liczb
Fibonacciego. W jzykach funkcyjnych jest to tradycyjny program typu „Witaj,
wiecie”. Liczby Fibonacciego omawiamy szczegóowo w podrozdziale 4.2,
„»Leniwe« podejcie”. Na razie upewnij si, e moesz uruchomi przykadow
funkcj
fibs
. Wprowad poniszy wiersz kodu w rodowisku REPL, a otrzy-
masz 10 pierwszych liczb Fibonacciego.
(take 10 examples.introduction/fibs)
-> (0 1 1 2 3 5 8 13 21 34)
Jeli otrzymae 10 pierwszych liczb Fibonacciego (wymienionych powyej),
poprawnie zainstalowae przykadowy kod z ksiki.
Wszystkie przykady sprawdzilimy za pomoc testów jednostkowych (testy
znajduj si w katalogu examples/test). Samych testów nie omawiamy w ksice,
jednak mog okaza si przydatnym ródem wiedzy. Aby uruchomi testy jed-
nostkowe, uyj instrukcji
lein test
.
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
41
Instrukcje require i use
Po zadaniu biblioteki jzyka Clojure za pomoc instrukcji
require
elementy
z biblioteki trzeba wskazywa za pomoc penej nazwy. Zamiast nazwy
fibs
trzeba uy okrelenia
examples.introduction/fibs
. Uruchom drugi egzemplarz
rodowiska REPL
8
i wprowad poniszy kod.
(require 'examples.introduction)
-> nil
(take 10 examples.introduction/fibs)
-> (0 1 1 2 3 5 8 13 21 34)
Wprowadzanie penych nazw szybko staje si kopotliwe. Moesz uy instrukcji
refer
dla przestrzeni nazw i odwzorowa wszystkie nazwy z tej przestrzeni na
biec przestrze nazw.
(refer quoted-namespace-symbol)
Wywoaj instrukcj
refer
dla przestrzeni
examples.introduction
i sprawd, czy
moesz bezporednio wywoa funkcj
fibs
.
(refer 'examples.introduction)
-> nil
(take 10 fibs)
-> (0 1 1 2 3 5 8 13 21 34)
Wygodna funkcja
use
pozwala wykona instrukcje
require
i
refer
w jednym kroku.
(use quoted-namespace-symbol)
W nowym rodowisku REPL wprowad nastpujce instrukcje.
(use 'examples.introduction)
-> nil
(take 10 fibs)
-> (0 1 1 2 3 5 8 13 21 34)
W trakcie pracy z przykadowym kodem z ksiki moesz wywoa instrukcj
require
lub
use
z opcj
:reload
, aby wymusi ponowne wczytanie biblioteki.
(use :reload 'examples.introduction)
-> nil
Opcja
:reload
jest przydatna, jeli wprowadzasz zmiany i chcesz sprawdzi ich
efekt bez ponownego uruchamiania rodowiska REPL.
8
Otwarcie nowego rodowiska REPL zapobiega konfliktom nazw midzy utworzonym
wczeniej kodem a funkcjami o tych samych nazwach z przykadowego kodu. W praktyce
nie stanowi to problemu. Zagadnienie to omawiamy w punkcie „Przestrzenie nazw”.
Kup książkę
Poleć książkę
42
Programowanie w jzyku Clojure
Znajdowanie dokumentacji
Potrzebna dokumentacja czsto jest dostpna bezporednio w rodowisku REPL.
Najprostsz funkcj pomocnicz
9
jest
doc
.
(doc name)
Uyjmy funkcji
doc
do wywietlenia dokumentacji funkcji
str
.
user=> (doc str)
-------------------------
clojure.core/str
([] [x] [x & ys])
With no args, returns the empty string. With one arg x, returns
x.toString(). (str nil) returns the empty string. With more than
one arg, returns the concatenation of the str values of the args.
Pierwszy wiersz danych wyjciowych funkcji
doc
obejmuje pen nazw spraw-
dzanej funkcji. W drugim znajduj si argumenty generowane bezporednio
w kodzie. Wybrane czsto stosowane nazwy argumentów i ich zastosowanie
omawiamy w ramce „Zwyczajowe nazwy parametrów”. Dalsze wiersze obejmuj
acuch znaków dokumentacji, jeli jest on podany w definicji funkcji.
Zwyczajowe nazwy parametrów
acuchy znaków dokumentacji w funkcjach
reduce
i
areduce
obejmuj
szereg krótkich nazw parametrów. Oto niektórych z tych nazw i sposoby
ich stosowania.
Parametr
Zastosowanie
a
Tablica Javy
agt
Agent
coll
Kolekcja
expr
Wyraenie
f
Funkcja
idx
Indeks
r
Referencja
v
Wektor
val
Warto
Nazwy mog wydawa si krótkie, jednak jest tak nie bez powodu —
„dobre” nazwy czsto s ju „zajte” przez funkcje jzyka Clojure! Uy-
wanie dla parametrów nazw identycznych z nazwami funkcji jest dopusz-
czalne, ale uznaje si to za oznak zego stylu. Parametr zasania wtedy
funkcj, dlatego jest ona niedostpna, kiedy parametr znajduje si w za-
sigu programu. Dlatego nie naley nazywa referencji
ref
, agentów
agent
, a liczników —
count
, poniewa s to nazwy funkcji.
9
Tak naprawd
doc
to makro jzyka Clojure.
Kup książkę
Poleć książkę
Rozdzia 1. • Wprowadzenie
43
acuch znaków dokumentacji mona doda do funkcji przez umieszczenie go
bezporednio po jej nazwie.
src/examples/introduction.clj
(defn hello
"Wywietla powitanie na wyjciu, uywajc nazwy uytkownika. "
[username]
(println (str "Witaj, " username))
Czasem nie znasz nazwy elementu, którego dokumentacji potrzebujesz. Funkcja
find-doc
wyszukuje informacje o wszystkich elementach, dla których dane wyj-
ciowe funkcji
doc
pasuj do przekazanego wyraenia regularnego lub acucha
znaków.
(find-doc s)
Za pomoc funkcji
find-doc
mona sprawdzi, w jaki sposób Clojure skraca
kolekcje.
user=> (find-doc "reduce")
-------------------------
clojure/areduce
([a idx ret init expr])
Macro
... Szczegóy pominito ...
-------------------------
clojure/reduce
([f coll] [f val coll])
... Szczegóy pominito ...
Funkcja
reduce
pozwala w skrócony sposób stosowa operacje do kolekcji
jzyka Clojure. Omawiamy j w punkcie „Przeksztacanie sekwencji”. Funkcja
areduce
wspódziaa z tablicami Javy, a opisujemy j w punkcie „Korzystanie
z kolekcji Javy”.
Dua cz jzyka Clojure jest napisana w nim samym, dlatego lektura jego
kodu ródowego to pouczajce zadanie. Kod ródowy funkcji jzyka Clojure
mona wywietli za pomoc instrukcji
source
z biblioteki
repl
.
(clojure.repl/source symbol)
Wywietlmy kod ródowy prostej funkcji
identity
.
(use 'clojure.repl)
(source identity)
-> (defn identity
"Returns its argument."
{:added "1.0"
:static true}
[x] x)
Kup książkę
Poleć książkę
44
Programowanie w jzyku Clojure
Oczywicie, mona te uywa interfejsu API Reflection Javy. Metody
class
,
ancestors
,
instance?
i podobne pozwalaj sprawdzi model obiektowy Javy
i informuj na przykad o tym, e kolekcje jzyka Clojure s jednoczenie ko-
lekcjami Javy.
(ancestors (class [1 2 3]))
-> #{clojure.lang.ILookup clojure.lang.Sequential
java.lang.Object clojure.lang.Indexed
java.lang.Iterable clojure.lang.IObj
clojure.lang.IPersistentCollection
clojure.lang.IPersistentVector clojure.lang.AFn
java.lang.Comparable java.util.RandomAccess
clojure.lang.Associative
clojure.lang.APersistentVector clojure.lang.Counted
clojure.lang.Reversible clojure.lang.IPersistentStack
java.util.List clojure.lang.IEditableCollection
clojure.lang.IFn clojure.lang.Seqable
java.util.Collection java.util.concurrent.Callable
clojure.lang.IMeta java.io.Serializable java.lang.Runnable}
Internetow dokumentacj interfejsu API jzyka Clojure znajdziesz na stronie
http://clojure.github.com/clojure. W ramce widocznej w prawej czci tej strony
znajduj si odnoniki do wszystkich funkcji i makr. Po lewej stronie umiesz-
czono odnoniki do artykuów na temat rónych cech jzyka Clojure.
1.4. Podsumowanie
Wanie zakoczye szybki przegld jzyka Clojure. Poznae dajc due
moliwoci skadni tego jzyka i jego zwizki z Lispem, a take zobaczye, jak
atwe jest wywoywanie w Clojure kodu Javy.
Uruchomie jzyk Clojure w swoim rodowisku, a take napisae w rodowisku
REPL krótkie programy ilustrujce programowanie funkcyjne i sucy do obsugi
stanu model referencji. Pora przyjrze si caemu jzykowi.
Kup książkę
Poleć książkę
Skorowidz
A
agenty, 158
bieca warto, 159
sprawdzanie poprawnoci, 160
transakcje, 161
tworzenie, 158
wykrywanie bdów, 160
algebra relacji, 110
Apache Ant, 244
atomy, 157
dereferencja, 157
tworzenie, 157
ustawienie wartoci, 157
B
biblioteki, 20, 40
dosowne podawanie nazwy, 40
drzewo waciwoci systemowych, 238
znajdowanie dokumentacji, 42
C
Clojure, 15, 23
aktualizacja, 261
aspekty, 23
biblioteki, 20, 40
drzewo waciwoci systemowych, 238
inspector, 238
Lazytest, 286
math.combinatorics, 275
Midje, 286
sekwencje, 87
test, 239
test.generative, 278
cechy jzyka, 24, 209
czytnik, 46
makra odczytu, 55
edytory kodów, 297
funkcje, 56
czyste, 38
dodawanie sugestii typów, 253
impure, 38
wywoanie, 56
wyszego rzdu, 25
Java, 15, 33, 243
dostp, 243
interfejsy, 183, 255
javadoc, 70
kompilacja AOT, 248
konstrukcja new, 68
korzystanie z kolekcji, 258
mechanizm wywoa zwrotnych, 256
parsery SAX, 255
rodowisko uruchomieniowe, 34
tworzenie klas, 255
Kup książkę
Poleć książkę
302
Programowanie w jzyku Clojure
Clojure
Java
tworzenie poredników, 255
tworzenie tablic, 258
wykorzystanie moliwoci, 247
wywoywanie kodu, 68
wywoywanie metody, 69
jzyk funkcyjny, 30
cechy, 30
konstrukcje skadniowe, 24, 46
Leiningen, 34, 261
liczby cakowite, 248
operatory, 248
Lisp, 15, 24, 27
makra, 28, 201
taksonomia, 216
maszyny JVM, 29
metadane, 77
model czasu, 24
niezmienne struktury danych, 27
obsuga wyjtków, 244
kontrolowane wyjtki, 245
porzdkowanie zasobów, 245
reagowanie na wyjtki, 247
optymalizowanie wydajnoci, 250
dodawanie sugestii typów, 253
uywanie typów prostych, 250
pobieranie zalenoci, 261
poczenie z witryn, 261
programowanie, 16
funkcyjne, 18, 24, 115
ptle, 74
reguy, 120
wspóbiene, 32
protokoy, 24, 179, 184
przestrze nazw, 36, 61, 65
rejestrowanie informacji, 264
rekordy, 193
rozkadanie struktury, 63
moliwoci, 65
sekwencje, 29, 81
biblioteka, 87
cechy, 83
Java, 98
manipulowanie, 197
wymuszanie realizacji, 97
wyraenia listowe, 95
stan, 147
model aktualizacji, 163
model funkcyjny, 149
model referencyjny, 149
sterowanie przepywem, 70
instrukcje, 70
makra, 71
rekurencja, 72
rodowisko REPL, 35
zmienne specjalne, 36
tosamo, 147
typy referencyjne, 147
tworzenie aplikacji, 269
gra Clojurebreaker, 270
typy danych, 179, 188
cechy, 188
typy referencyjne, 38
atom, 38
warto, 147
wizania, 61
wielometody, 225
wspóbieno, 18, 32, 148
powody stosowania, 148
sytuacja wycigu, 149
zakleszczenie, 149
zmienne, 36, 61
cechy, 62
D
definicja pustego acucha znaków, 26
duck typing, 250
F
funkcje, 56
anonimowe, 58
konstrukcja, 59
powody tworzenia, 58
stosowanie, 61
unikanie, 223
Kup książkę
Poleć książkę
Skorowidz
303
bez sprawdzania przepenienia, 251
czciowe wywoanie, 134
czyste, 38, 116
niezmienne dane, 116
dodawanie sugestii typów, 253
efekty uboczne, 71
funkcje wyszego rzdu, 25
leniwe, 127
liczba argumentów, 57
listy, 105
acuchy znaków dokumentacji, 42
odwzorowania, 53, 106
tworzenie, 108
operatory matematyczne, 47
predykaty, 52, 56
przejrzyste referencyjnie, 118
memoizacja, 119
rozwinicie funkcji, 135
implementacja, 135
sekwencje, 83
filtrowanie, 91
predykaty, 92
przeksztacanie, 93
tworzenie, 88
wyraenia regularne, 100
sowa kluczowe, 54
wektory, 105
wizania, 62
zasig leksykalny, 63
wywoanie, 47, 56
notacja przedrostkowa, 47
notacja wrostkowa, 47
zbiory, 109
zoone, 134
H
hermetyzacja, 119
efekty uboczne, 120
Heroku, 292
biblioteka, 293
git init, 293
git push, 294
plik Procfile, 292
polecenie heroku, 293
rejestracja konta, 292
repozytorium git, 293
I
instrukcje
add-points, 171
agent-errors, 160
alias, 233
alter, 152
assoc, 171
atom, 157
binding, 164
clear-agent-errors, 160
commute, 154
concat, 210
cond, 227
condp, 182
conj, 38, 86
cons, 83, 132, 171
def, 38, 61, 218
defmacro, 203
defmethod, 228
defmulti, 175, 228
defn, 57
defonce, 133
defrecord, 27, 54
deref, 39, 150
derive, 236
dir, 279
do, 71, 219
doall, 97
dosync, 33, 150
faux-curry, 135
file-seq, 101
first, 76, 83
fn, 58
for, 75
force, 221
if, 70, 202
import, 67, 199
in-ns, 66, 281
inspect-tree, 238
Kup książkę
Poleć książkę
304
Programowanie w jzyku Clojure
instrukcje
lazy-cat, 129
lazy-seq, 142
lein deps, 261
lein test, 40
let, 212
line-seq, 102
loop, 72
loop/recur, 72
macroexpand, 207
map, 219
memoize, 165
next-counter, 155
partial, 134
partition, 132
prefer-method, 232
println, 116, 203
project, 112
proxy, 176
pst, 37
recur, 72, 120, 130
ref, 156
refer, 41
ref-set, 150
require, 40, 41, 279
reset!, 157
rest, 83
score print-table, 276
select, 112
send, 159
send-off, 161
seq, 101, 258
set, 90
set!, 167, 254
source, 43
start, 163
swap!, 158
take-while, 91
trampoline, 139
unless, 202
use, 41, 66
var, 62
with-open, 102
J
Java, 15, 16, 243
interfejsy, 183
wady, 183
zalety, 183
klasy
BigDecimal, 249
BigInteger, 249
Character, 50
ChunkedSeq, 84
Person, 26
Random, 68
StringUtils, 74
Thread, 33
WidgetFactory, 205
metody egzemplarza, 204
obsuga, 245
obsuga wyjtków, 244
kontrolowane wyjtki, 244
porzdkowanie zasobów, 245
sekwencje, 98
kolekcje sekwencyjne, 98
wywoywanie kodu, 68
K
kod gry Snake, 169
interfejs GUI, 169, 175
aktualizowanie, 175
defmulti, 175
fill-point, 175
game, 176
game-panel, 175
paint, 175
proxy, 176
tworzenie nowej gry, 176
wywietlanie wa i jabka, 175
model funkcyjny, 169
add-points, 170, 171
assoc, 171
cons, 171
dodawanie punktów, 170
eats?, 172
Kup książkę
Poleć książkę
Skorowidz
305
head-overlaps-body?, 172
lose?, 172
move, 171
point-to-screen-rect, 171
ruch wa, 171
sprawdzanie warunków zwycistwa, 172
turn, 173
tworzenie nowego jabka, 171
tworzenie wa, 171
tworzenie zestawu staych, 169
win?, 172
wykrywanie zetknicia, 172
zjadanie jabka, 172
zmiana kierunku wa, 173
model zmiennego stanu, 169, 173
reset-game, 173
update-direction, 174
update-positions, 174
wyduanie wa, 174
zmiany pozycji wa i jabka, 173
konstrukcje skadniowe, 24
liczba, 46
BigDecimal, 48
BigInt, 48
cakowita, 48
Ratio, 48
wektor, 47
zmiennoprzecinkowa, 48
lista, 46, 47
wywoywanie funkcji, 47
acuch znaków, 46, 49
wywoanie, 50
odwzorowania, 46, 52
sekwencje, 85
rekordy, 52
wywoywanie, 54
sowo kluczowe, 46, 53
symbol, 46, 49
warto logiczna, 46
reguy dziaania, 51
warto nil, 46
wektory, 46
sekwencje, 84
zbiory, 46
sekwencje, 85
znak, 46
L
Leiningen, 34, 261
pobieranie zalenoci, 35
wtyczka lein-noir, 287
leniwe sekwencje, 121, 125
moment realizacji, 127
Lisp, 15, 24
M
makra, 28, 201
amap, 260
and, 207, 216
areduce, 260
assert, 222
bad-unless, 207
bench, 212
binding, 164, 221
chain, 209
comment, 217
cond, 30
czas kompilacji, 203
czas rozwijania makra, 203
declare, 137, 218
definterface, 183
defpartial, 288
defprotocol, 185
defrecord, 28, 193
defstruct, 218
deftype, 189
delay, 220
dosync, 221
dotimes, 250
extend-protocol, 186
extend-type, 186
for, 95
import-static, 220
is, 239
jzyk szablonów, 210
Kup książkę
Poleć książkę
306
Programowanie w jzyku Clojure
makra
konstrukcje specjalne, 215
lazy-seq, 125
let, 221
letfn, 124
manipulowanie list, 210
ns, 67
obsuga kilku konstrukcji, 208
obsuga szablonów, 211
przechwytywanie symboli, 213
splicing unquote, 211
unquote, 211
przetwarzanie, 203
reguy stosowania, 202
reify, 198
rozwijanie, 206
rekurencyjne, 207
tworzenie, 212
sprawdzanie, 206
sterowanie przebiegiem programu, 202
tablice Javy, 260
taksonomia, 214
kategorie, 216
time, 212, 221
tworzenie, 203
tworzenie nazw, 212
nazwy lokalne, 214
tworzenie zmiennych, 218
unikanie funkcji anonimowych, 223
unless, 203
upraszczanie, 209
wartociowanie argumentów, 203, 206
nakadki, 221
odraczanie, 220
warunkowe, 216
when, 208
when-not, 202, 208
with-open, 221, 245
with-out-str, 221
wspódziaanie z Jav, 219
wzorzec projektowy, 205
makra odczytu, 55
dereferencja, 56
funkcja anonimowa, 56
komentarz, 55, 56
metadane, 56
podawanie zmiennych, 56
przytaczanie, 56
syntax-quote, 56
unquote, 56
unquote-slicing, 56
wzorzec wyraenia regularnego, 56
maszyny JVM, 15, 29
autorekurencja, 124
Clojure, 16
Java, 16
memoizacja, 165
metadane, 77
standardowe klucze, 78
model czasu, 24
N
niezmienne struktury danych, 27
O
optymalizacja TCO, 73, 123
P
pami STM, 32, 151
technika MVCC, 153
transakcje, 151
ACI, 151
aktualizacje, 151
polimorfizm, 231
programowa pami transakcyjna, Patrz
pami STM
programowanie, 16, 18, 31, 97, 148
funkcyjne, 18, 24, 30, 116
autorekurencja, 124
czyste funkcje, 116
definicje rekurencyjne, 121
leniwe podejcie, 118, 121
leniwe sekwencje, 121, 125
problemy rekurencyjne, 138
prosta rekurencja, 122
Kup książkę
Poleć książkę
Skorowidz
307
reguy, 120
rekurencja, 118
rekurencja kocowa, 123
rekurencja wzajemna, 136
trwae struktury danych, 117
wielokrotne wykorzystanie kodu, 119
wspóuytkowanie struktur, 117
wyraenie listowe, 31
zalety, 119
imperatywne, 31
kod ptli, 74
prawidowy proces pisania kodu, 273
sekwencyjne, 97
testowanie kodu, 274
BDD, 286
generowanie danych testu, 279
podstawowe etapy, 278
programowe sprawdzanie poprawnoci,
280
przeprowadzanie testów, 276, 283
sprawdzanie poprawnoci danych
wyjciowych, 277
sprawdzanie poprawnoci kodu, 278
TDD, 286
testy jednostkowe, 286
testy regresji, 277
tworzenie danych wejciowych, 275
zgaszanie bdu, 284
wspóbiene, 148
powody stosowania, 148
wykorzystanie abstrakcji, 180
obsuga dodatkowych typów, 181
odczyt, 180
zapis, 180
protokoy, 24, 179, 184
zalety, 184
przestrze nazw, 36, 65
clojure.core, 66
clojure.string, 67
funkcje, 68
modyfikacja deklaracji, 264
myapp, 66
user, 36, 65
wizania, 65
R
referencje, 150
sprawdzanie poprawnoci, 156
transakcje, 150
ACID, 151
aktualizowanie informacji, 152
atomowe, 151
izolowane, 151
licznik, 155
pami STM, 151
skoordynowane, 151
spójne, 151
trwae, 151
warto wewntrztransakcyjna, 153
waciwoci, 151
zmiana encji, 150
tworzenie, 150
rekordy, 193
dodawanie metod, 195
dostp do pól, 194
implementacja protokou, 195
Note, 193
odwzorowania, 193
Person, 26
tworzenie, 193
rekurencja, 72, 118
autorekurencja, 124
definicje, 121
indukcja, 121
przypadek bazowy, 121
problemy rekurencyjne, 138
prosta rekurencja, 122
przyspieszanie, 143
rekurencja kocowa, 123
optymalizacja TCO, 123
rekurencja wzajemna, 136
memoizacja, 144
optymalizowanie, 139
przeksztacanie na autorekurencj, 138
zastpowanie leniwym podejciem, 141
Kup książkę
Poleć książkę
308
Programowanie w jzyku Clojure
S
sekwencje, 29, 81
biblioteka, 86, 87
funkcje, 88
cechy, 83
filtrowanie, 91
funkcje, 91
funkcje, 83
into, 86
Java, 98
kolekcje sekwencyjne, 82, 98
niezmienne, 87
odwzorowania, 85
predykaty, 92
przeksztacanie, 93
funkcje, 93
strumienie, 102
system plików, 101
funkcje, 101
tryb leniwy, 86, 96
stosowanie, 97
zalety, 96
tworzenie, 88
funkcje, 88
wektory, 84
wymuszanie realizacji, 97
wyraenia regularne, 100
funkcje, 100
zbiory, 85
stan, 147
agenty, 147, 158
bieca warto, 159
sprawdzanie poprawnoci, 160
transakcje, 161
tworzenie, 158
wykrywanie bdów, 160
atomy, 147, 157
dereferencja, 157
tworzenie, 157
ustawienie wartoci, 157
model aktualizacji, 163
modele zarzdzania, 168
referencje, 147, 150
transakcje, 150
tworzenie, 150
wywoywane zwrotnie metody, 166
zmienne, 147, 163
rodowisko REPL, 35
zmienne specjalne, 36
T
tworzenie aplikacji, 269
instalowanie kodu, 292
git push, 292, 294
Heroku, 292
plik Procfile, 292
testowanie kodu wyniku, 274
defspec, 282
dir, 279
failures, 284
generate-test-data, 282
generowanie danych testu, 279
in-ns, 281
programowe sprawdzanie poprawnoci,
280
random-secret, 280
require, 279
score print-table, 276
score-inputs, 278
selections, 275
sprawdzanie poprawnoci danych
wyjciowych, 277
test dla danych wejciowych, 276
test-dirs, 283
test-namespaces, 283
test-vars, 283
testy regresji, 277
tworzenie danych wejciowych, 275
zgaszanie bdu, 284
tworzenie interfejsu, 287
board, 290
defpartial, 288
dodanie stanu, 287
Kup książkę
Poleć książkę
Skorowidz
309
framework sieciowy, 287
interfejs gracza, 288
render, 290
session/flash-put!, 290
session/get, 287
session/put!, 287
session/remove!, 290
wynik, 270
exact-matches, 271
frequencies, 271
generowanie, 270
merge-with, 272
score, 273
select-keys, 272
unordered-matches, 272
typy danych, 179, 188
anonimowe egzemplarze, 198
cechy, 188
CryptoVault, 179, 188
definiowanie metod, 189
dostp do pól, 189
rekordy, 193
tworzenie, 189
typy referencyjne, 38
atom, 38
W
wizania, 62
bindings, 63
dynamiczne, 164
modyfikacje na odlego, 164
dynamicznie okrelany zasig, 164
funkcje, 62
zasig leksykalny, 63
lokalne, 164
mechanizm rozkadania struktury, 63
podstawowe, 61
przestrze nazw, 65
zmienne, 62
wielometody, 225
account-level, 235
assert-expr, 240
definiowanie, 228
dodawanie implementacji metod, 228
domylne dziaanie, 230
dorane taksonomie, 233
dziedziczenie, 236
mechanizm wybierania implementacji, 229
my-print, 228, 231
paint, 175
service-charge, 235
stosowanie, 237
reguy, 241
wybieranie metod, 229
dla typu kolekcji, 231
na podstawie typu pierwszego
argumentu, 231
rozwizywanie konfliktów, 232
wspóbieno, 32
wyraenie listowe, 94
elementy, 94
Z
zmienne, 36, 61, 163
aliasy, 62
cechy, 62
mechanizm rozkadania struktury, 63
metadane, 62
rodzaje definicji, 133
specjalne, 164
modyfikacje na odlego, 164
tworzenie, 133
makra, 218
wizania, 62
lokalne dla wtku, 164
podstawowe, 61
Kup książkę
Poleć książkę
310
Programowanie w jzyku Clojure
Kup książkę
Poleć książkę