Darmowy fragment publikacji:
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.
• Kup książkę
• Poleć książkę
• Oceń książkę
• Księgarnia internetowa
• Lubię to! » Nasza społeczność
Spis treĂci
PodziÚkowania ............................................................................................10
Przedmowa do wydania drugiego .............................................................11
Przedmowa do wydania pierwszego ........................................................13
WstÚp ...........................................................................................................15
Rozdziaï 1. Wprowadzenie .........................................................................23
1.1. Dlaczego Clojure? .............................................................................................24
1.2. Szybkie wprowadzenie do programowania w Clojure ...........................................34
1.3. Biblioteki jÚzyka Clojure .....................................................................................40
1.4. Podsumowanie ..................................................................................................44
Rozdziaï 2. PrzeglÈd jÚzyka Clojure ..........................................................45
2.1. Konstrukcje skïadniowe ......................................................................................46
2.2. Makra odczytu ...................................................................................................55
2.3. Funkcje .............................................................................................................56
2.4. Zmienne, wiÈzania i przestrzenie nazw ...............................................................61
2.5. Wywoïywanie kodu Javy .....................................................................................68
2.6. Przepïyw sterowania ..........................................................................................70
2.7. Gdzie siÚ podziaïa pÚtla for? ..............................................................................74
2.8. Metadane ..........................................................................................................77
2.9. Podsumowanie ..................................................................................................79
8
Programowanie w jÚzyku Clojure
Rozdziaï 3. Ujednolicanie danych za pomocÈ sekwencji .......................81
3.1. Wszystko jest sekwencjÈ .....................................................................................83
3.2. Stosowanie biblioteki sekwencji ...........................................................................87
3.3. Sekwencje nieskoñczone 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” podejĂcie? ......................................................................121
4.3. Leniwsze niĝ leniwe .........................................................................................130
4.4. Jeszcze o rekurencji ..........................................................................................136
4.5. Podsumowanie ................................................................................................146
Rozdziaï 5. Stan .........................................................................................147
5.1. WspóïbieĝnoĂÊ, równolegïoĂÊ 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. ZarzÈdzanie stanem specyficznym dla wÈtku za pomocÈ zmiennych ....................163
5.6. Gra Snake w jÚzyku Clojure .............................................................................168
5.7. Podsumowanie ................................................................................................178
Rozdziaï 6. Protokoïy i typy danych .......................................................179
6.1. Programowanie z wykorzystaniem abstrakcji ......................................................180
6.2. Interfejsy .........................................................................................................183
6.3. Protokoïy ........................................................................................................184
6.4. Typy danych ...................................................................................................188
6.5. Rekordy ..........................................................................................................193
6.6. Makro reify .....................................................................................................198
6.7. Podsumowanie ................................................................................................199
Spis treĂci
9
Rozdziaï 7. Makra .....................................................................................201
7.1. Kiedy naleĝy 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. WiÚcej niĝ proste wybieranie metod ...................................................................231
8.4. Tworzenie doraěnych taksonomii ......................................................................233
8.5. Kiedy naleĝy korzystaÊ z wielometod? ...............................................................237
8.6. Podsumowanie ................................................................................................241
Rozdziaï 9. Sztuczki z JavÈ ......................................................................243
9.1. Obsïuga wyjÈtków ............................................................................................244
9.2. Zmagania z liczbami caïkowitymi .....................................................................248
9.3. Optymalizowanie wydajnoĂci ............................................................................250
9.4. Tworzenie klas Javy w jÚzyku Clojure ................................................................255
9.5. Praktyczny przykïad .........................................................................................261
9.6. Podsumowanie ................................................................................................268
Rozdziaï 10. Tworzenie aplikacji ............................................................269
10.1. Wynik w grze Clojurebreaker ..........................................................................270
10.2. Testowanie kodu zwracajÈcego wynik ..............................................................274
10.3. Biblioteka test.generative ................................................................................278
10.4. Tworzenie interfejsu .......................................................................................287
10.5. Instalowanie kodu ..........................................................................................292
10.6. Poĝegnanie ....................................................................................................295
Dodatek A. Edytory kodu .........................................................................297
Dodatek B. Bibliografia ............................................................................299
Skorowidz ..................................................................................................301
22
Programowanie w jÚzyku Clojure
Rozdziaï 1.
Wprowadzenie
S
zybki wzrost popularnoĂci jÚzyka Clojure wynika z wielu przyczyn. Po krót-
kich poszukiwaniach w sieci WWW dowiesz siÚ, ĝe Clojure:
jest jÚzykiem funkcyjnym;
jest Lispem na maszyny JVM;
ma specjalne mechanizmy do obsïugi wspóïbieĝnoĂci.
Wszystkie te cechy sÈ waĝne, jednak ĝadna z nich nie odgrywa najwaĝniejszej
roli. Naszym zdaniem najistotniejszymi aspektami jÚzyka Clojure sÈ jego pro-
stota i moĝliwoĂci.
Prostota w kontekĂcie oprogramowania jest waĝna z kilku wzglÚdów, tu jednak
mamy na myĂli pierwotne i najwaĝniejsze znaczenie tego sïowa — proste jest
to, co nie jest zïoĝone. Proste komponenty umoĝliwiajÈ systemowi przeprowa-
dzanie operacji zaplanowanych przez projektantów i nie wykonujÈ czynnoĂci
niepowiÈzanych z danym zadaniem. Z naszych doĂwiadczeñ wynika, ĝe nie-
wielka zïoĝonoĂÊ zwykle szybko przeksztaïca siÚ w niebezpiecznie powaĝnÈ.
Takĝe sïowo moĝliwoĂci ma wiele znaczeñ. Tu mamy na myĂli zdolnoĂÊ do
wykonywania stawianych aplikacji zadañ. Aby programista miaï odpowiednie
moĝliwoĂci, musi wykorzystaÊ platformÚ, która sama je posiada i jest powszechnie
dostÚpna. TakÈ platformÈ jest na przykïad maszyna JVM. Ponadto uĝywane
narzÚdzia muszÈ zapewniaÊ peïny, nieograniczony dostÚp do oferowanych
moĝliwoĂci. DostÚp do moĝliwoĂci jest czÚsto podstawowym wymogiem w pro-
jektach, w których trzeba w peïni wykorzystaÊ platformÚ.
24
Programowanie w jÚzyku Clojure
Przez lata tolerowaliĂmy bardzo skomplikowane narzÚdzia, które byïy jedynym
sposobem na uzyskanie potrzebnych moĝliwoĂci. Czasem akceptowaliĂmy teĝ ogra-
niczone moĝliwoĂci w celu uproszczenia modelu programowania. Niekiedy nie da
siÚ uniknÈÊ pewnych kompromisów, jednak w obszarze moĝliwoĂci i prostoty nie
trzeba siÚ z nimi godziÊ. JÚzyk Clojure to dowód na to, ĝe cechy te moĝna poïÈczyÊ.
1.1. Dlaczego Clojure?
Wszystkie charakterystyczne cechy jÚzyka Clojure majÈ zapewniaÊ prostotÚ,
moĝliwoĂci lub i jedno, i drugie. Oto kilka przykïadów:
Programowanie funkcyjne jest proste, poniewaĝ pozwala oddzieliÊ ob-
liczenia od stanu i toĝsamoĂci. ZaletÈ jest to, ĝe programy funkcyjne sÈ
ïatwiejsze do zrozumienia, pisania, testowania, optymalizowania i rów-
nolegïego wykonywania.
Konstrukcje umoĝliwiajÈce wspóïdziaïanie jÚzyków Clojure i Java dajÈ
duĝe moĝliwoĂci, poniewaĝ zapewniajÈ bezpoĂredni dostÚp do skïadni
Javy. ZaletÈ jest to, ĝe moĝna uzyskaÊ wydajnoĂÊ na poziomie Javy
i stosowaÊ skïadniÚ charakterystycznÈ dla tego jÚzyka. Co waĝniejsze,
nie trzeba uciekaÊ siÚ do jÚzyka niĝszego poziomu, aby zapewniÊ sobie
dodatkowe moĝliwoĂci.
Lisp jest prosty w dwóch bardzo waĝnych aspektach — oddziela
wczytywanie od wykonania, a jego skïadnia obejmuje niewielkÈ liczbÚ
niezaleĝnych elementów. ZaletÈ jest to, ĝe wzorce projektowe sÈ ujÚte
w abstrakcyjne konstrukcje skïadniowe, a S-wyraĝenia obejmujÈ kod
w jÚzykach XML, JSON i SQL.
Lisp daje teĝ duĝe moĝliwoĂci, poniewaĝ udostÚpnia kompilator i sys-
tem makr dziaïajÈcy w czasie wykonywania programu. ZaletÈ jest to, ĝe
w Lispie wystÚpuje póěne wiÈzanie i moĝna ïatwo tworzyÊ jÚzyki DSL.
Model czasu w jÚzyku Clojure jest prosty. Oddzielono w nim wartoĂci,
toĝsamoĂÊ, stan i czas. ZaletÈ jest to, ĝe w programach moĝna spraw-
dzaÊ i zapamiÚtywaÊ informacje bez obaw o to, ĝe starsze dane zostanÈ
nadpisane.
Protokoïy sÈ proste. W Clojure polimorfizm jest niezaleĝny od dzie-
dziczenia. ZaletÈ jest to, ĝe moĝna bezpiecznie i w konkretnym celu
rozszerzaÊ typy oraz abstrakcje. Nie wymaga to stosowania zawiïych
wzorców projektowych lub wprowadzania zmian w cudzym kodzie
(co czÚsto prowadzi do bïÚdów).
Rozdziaï 1. • Wprowadzenie
25
Ta lista cech stanowi „mapÚ” pomocnÈ w dalszych rozdziaïach ksiÈĝki. Nie
martw siÚ, jeĂli na razie nie rozumiesz wszystkich szczegóïów. Kaĝdej z tych
cech poĂwiÚcamy caïy 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Ê wiÚksze przykïadowe programy, które prezentujemy dalej w ksiÈĝce.
JÚzyk Clojure jest elegancki
W jÚzyku Clojure stosunek sygnaïu do szumu jest wysoki. Dlatego programy
pisane w tym jÚzyku sÈ krótkie. Takie programy sÈ tañsze w tworzeniu, insta-
lowaniu i konserwacji1. Jest to prawdÈ zwïaszcza wtedy, gdy programy sÈ zwiÚ-
zïe, a nie tylko treĂciwe. Przyjrzyj siÚ na przykïad poniĝszemu 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 ïañcuch znaków jest pusty (nie zawiera ĝad-
nych znaków lub obejmuje same odstÚpy). Oto kod podobnej metody w jÚzyku
Clojure:
src/examples/introduction.clj
(defn blank? [str]
(every? #(Character/isWhitespace ) str))
Wersja w jÚzyku Clojure jest krótsza, a co waĝniejsze — prostsza. Nie wystÚ-
pujÈ tu zmienne, modyfikowalny stan ani rozgaïÚzienia. Efekt ten jest moĝliwy
dziÚki funkcjom wyĝszego rzÚdu. Funkcje tego rodzaju przyjmujÈ inne funkcje
1 Software Estimation: Demystifying the Black Art [McC06] to znakomita ksiÈĝka.
Jej autorzy dowodzÈ, ĝe krótsze jest tañsze.
26
Programowanie w jÚzyku Clojure
jako argumenty i (lub) zwracajÈ funkcje. Funkcja every? przyjmuje funkcjÚ
i kolekcjÚ, a zwraca wartoĂÊ true, jeĂli otrzymana funkcja zwraca true dla kaĝ-
dego elementu z kolekcji.
Poniewaĝ w wersji w jÚzyku Clojure nie ma rozgaïÚzieñ, kod jest bardziej czy-
telny i ïatwiejszy do przetestowania. Zalety te sÈ jeszcze wyraěniejsze w wiÚk-
szych programach. Ponadto, choÊ kod jest zwiÚzïy, moĝna go ïatwo zrozumieÊ.
Program w jÚzyku Clojure moĝna potraktowaÊ jak definicjÚ pustego ïañcucha
znaków — jest to ïañcuch, w którym kaĝdy znak jest odstÚpem. Kod ten jest
znacznie lepszy od metody z projektu Commons, w którym definicja pustego
ïañcucha znaków jest ukryta za szczegóïowym kodem pÚtli i instrukcji if.
Oto inny przykïad. Przyjrzyj siÚ banalnej klasie Person napisanej w jÚzyku 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 jÚzyku Clojure rekord Person moĝna zdefiniowaÊ w jednym wierszu:
(defrecord Person [first-name last-name])
KorzystaÊ z tego rekordu moĝna tak:
(def foo (- Person Aaron Bedra ))
- # user/foo
foo
- #:user.Person{:first-name Aaron , :last-name Bedra }
Rozdziaï 1. • Wprowadzenie
27
InstrukcjÚ defrecord i powiÈzane funkcje omawiamy w podrozdziale 6.3,
„Protokoïy”.
Kod w jÚzyku Clojure nie tylko jest znacznie krótszy; rekord Person jest tu nie-
zmienny. Niezmienne struktury danych sÈ z natury bezpieczne ze wzglÚdu
na wÈtki, a mechanizmy modyfikowania danych moĝna utworzyÊ w odrÚbnej
warstwie za pomocÈ referencji, agentów i atomów jÚzyka Clojure. Techniki te
omawiamy w rozdziale 5., „Stan”. Poniewaĝ rekordy sÈ niezmienne, jÚzyk
Clojure automatycznie udostÚpnia poprawne implementacje funkcji hashCode()
i equals().
JÚzyk Clojure ma wbudowanych wiele eleganckich rozwiÈzañ, jeĂli jednak
stwierdzisz, ĝe czegoĂ Ci w nim brakuje, moĝesz samodzielnie dodaÊ potrzebne
mechanizmy. UmoĝliwiajÈ to cechy Lispa.
Clojure to odĂwieĝony Lisp
Clojure jest Lispem. Od dziesiÚcioleci zwolennicy Lispa mówiÈ o przewagach,
jakie jÚzyk ten ma w porównaniu z wïaĂciwie wszystkimi innymi jÚzykami. Plan
opanowania Ăwiata przez Lispa jest jednak realizowany doĂÊ powoli.
Twórcy jÚzyka Clojure, podobnie jak autorzy kaĝdej odmiany Lispa, musieli
zmierzyÊ siÚ z dwoma problemami. Oto one:
Clojure ma odnieĂÊ sukces jako odmiana Lispa. Wymaga to przeko-
nania uĝytkowników Lispa, ĝe Clojure obejmuje najwaĝniejsze mecha-
nizmy swojego poprzednika.
JednoczeĂnie Clojure ma odnieĂÊ sukces tam, gdzie wczeĂniejsze wersje
Lispa siÚ nie przyjÚïy. Wymaga to zdobycia poparcia wiÚkszej spoïecz-
noĂci programistów.
Autorzy jÚzyka Clojure radzÈ sobie z tymi problemami przez udostÚpnienie me-
chanizmów metaprogramowania (charakterystycznych dla Lispa) i wprowadze-
nie zestawu usprawnieñ skïadniowych uïatwiajÈcych stosowanie jÚzyka Clojure
programistom, którzy nie znajÈ Lispa.
Dlaczego Lisp?
Wersje Lispa majÈ maïy rdzeñ, sÈ prawie pozbawione skïadni i majÈ rozbudo-
wane mechanizmy do obsïugi makr. Z uwagi na te cechy moĝna zmodyfikowaÊ
Lispa pod kÈtem projektu, zamiast dostosowywaÊ projekt do Lispa. Przyjrzyj siÚ
poniĝszemu fragmentowi kodu w Javie:
28
Programowanie w jÚzyku Clojure
public class Person {
private String firstName;
public String getFirstName() {
// CiÈg dalszy.
W kodzie wystÚpuje metoda getFirstName(). Metody sÈ polimorficzne i moĝna je
dostosowaÊ do potrzeb. Jednak znaczenie kaĝdego innego sïowa w przykïado-
wym kodzie jest okreĂlane przez jÚzyk. Czasem wygodna jest moĝliwoĂÊ zmiany
znaczenia poszczególnych sïów. Pozwala to na przykïad:
zdefiniowaÊ sïowo private jako „prywatne w kodzie produkcyjnym, ale
publiczne na potrzeby serializacji i testów jednostkowych”;
zdefiniowaÊ sïowo class w taki sposób, aby platforma automatycznie
generowaïa metody pobierajÈce i ustawiajÈce dla pól prywatnych (o ile
programista nie zarzÈdzi inaczej);
utworzyÊ podklasÚ dla class i umieĂciÊ w niej wywoïywane zwrotnie
uchwyty dla zdarzeñ cyklu ĝycia; klasa z obsïugÈ cyklu ĝycia moĝe na
przykïad zgïaszaÊ zdarzenie utworzenia egzemplarza tej klasy.
NatykaliĂmy siÚ na programy, w których potrzebne byïy wszystkie te cechy. Bez
potrzebnych mechanizmów programiĂci musieli uciekaÊ siÚ do powtarzalnych
i podatnych na bïÚdy sztuczek. Aby poradziÊ sobie z brakiem niezbÚdnych
rozwiÈzañ, napisano dosïownie miliony wierszy kodu.
W wiÚkszoĂci jÚzyków trzeba poprosiÊ osoby odpowiedzialne za implementacjÚ,
aby dodaïy wspomniane wczeĂniej mechanizmy. W Clojure moĝesz samodzielnie
dodaÊ potrzebne cechy, piszÈc makra (rozdziaï 7., „Makra”). Nawet sam jÚzyk
Clojure jest zbudowany za pomocÈ makr, takich jak defrecord:
(defrecord name [arg1 arg2 arg3])
JeĂli chcesz zmieniÊ znaczenie sïowa, moĝesz napisaÊ wïasne makro. Jeĝeli
potrzebujesz rekordów o Ăcisïej kontroli typów i z opcjonalnym sprawdzaniem,
czy pola nie majÈ wartoĂci null, wystarczy utworzyÊ wïasne makro defrecord.
Powinno ono wyglÈdaÊ tak:
(defrecord name [Type :arg1 Type :arg2 Type :arg3]
:allow-nulls false)
MoĝliwoĂÊ zmiany dziaïania jÚzyka w nim samym jest wyjÈtkowÈ zaletÈ Lispa.
Moĝna znaleěÊ wiele opisów róĝnych aspektów tego podejĂcia:
Lisp jest homoikoniczny2. 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ĂÊ
Rozdziaï 1. • Wprowadzenie
29
Caïy jÚzyk jest dostÚpny w kaĝdym momencie. Paul Graham w artykule
Revenge of the Nerds3 wyjaĂnia, dlaczego jest to waĝne.
Skïadnia Lispa eliminuje teĝ problemy z priorytetami operatorów i ïÈcznoĂciÈ
operacji. W ksiÈĝce tej nie znajdziesz tabel dotyczÈcych tych zagadnieñ. Z uwagi
na wymóg stosowania nawiasów wyraĝenia sÈ jednoznaczne.
WadÈ prostej, jednolitej skïadni Lispa (przynajmniej dla poczÈtkujÈcych) jest
nacisk na nawiasy i listy (listy to gïówny typ danych w Lispie). Clojure udo-
stÚpnia ciekawe poïÈczenie cech, które uïatwiajÈ uĝywanie Lispa programistom
znajÈcym inne jÚzyki.
Lisp z mniejszÈ liczbÈ nawiasów
Clojure zapewnia istotne zalety programistom uĝywajÈcym innych odmian Lispa.
Oto te korzyĂci:
W jÚzyku Clojure fizyczne listy z Lispa sÈ uogólnione do abstrakcyjnej
postaci — sekwencji. Pozwala to zachowaÊ moĝliwoĂci, jakie dajÈ listy,
a przy tym wykorzystaÊ ich zalety w innych strukturach danych.
Wykorzystanie maszyn JVM jako podstawy dla kodu w jÚzyku Clojure
daje dostÚp do standardowej biblioteki i bardzo popularnej platformy.
Sposób analizowania symboli i cudzysïowów sprawia, ĝe w jÚzyku
Clojure pisanie standardowych makr jest stosunkowo proste.
Wielu uĝytkowników jÚzyka Clojure nie zna Lispa, za to prawdopodobnie
sïyszaïo niepochlebne opinie na temat stosowania w nim nawiasów. W jÚzyku
Clojure zachowano nawiasy (i moĝliwoĂci Lispa!), jednak pod kilkoma wzglÚ-
dami usprawniono tradycyjnÈ skïadniÚ Lispa.
Clojure zapewnia wygodnÈ, opartÈ na literaïach skïadniÚ dla róĝnych
struktur danych (nie tylko dla list), takich jak wyraĝenia regularne, od-
wzorowania, zbiory, wektory i metadane. Dlatego kod w jÚzyku Clojure
jest mniej zaleĝny od list niĝ w wiÚkszoĂci innych odmian Lispa. MiÚdzy
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 uïatwia czytanie definicji funkcji w jÚzyku Clojure.
3 http://www.paulgraham.com/icad.html
30
Programowanie w jÚzyku Clojure
W jÚzyku Clojure, inaczej niĝ w wiÚkszoĂci odmian Lispa, przecinki
sÈ traktowane jak odstÚpy.
; Wektory przypominajÈ tablice z innych jÚzyków.
[1, 2, 3, 4]
- [1 2 3 4]
JÚzyk Clojure jest idiomatyczny i nie wymaga niepotrzebnego zagnieĝ-
dĝania nawiasów. Przyjrzyj siÚ makru cond, które wystÚpuje zarówno
w Common Lispie, jak i w Clojure. Makro cond sprawdza zestaw par
test-wynik i zwraca pierwszy wynik, dla którego wartoĂÊ wyraĝenia te-
stowego to true. Kaĝda para test-wynik znajduje siÚ w nawiasach:
; Makro cond w Common Lispie.
(cond ((= x 10) equal )
(( x 10) more ))
W jÚzyku Clojure dodatkowe nawiasy sÈ niepotrzebne.
; Makro cond w Clojure.
(cond (= x 10) equal
( x 10) more )
Jest to kwestia czysto estetyczna i oba podejĂcia majÈ swoich zwolenników.
Waĝne jest to, ĝe jÚzyk Clojure pozbawiono uciÈĝliwych aspektów Lispa,
jeĂli byïo to moĝliwe bez utraty zalet tego ostatniego.
Clojure jest ĂwietnÈ odmianÈ Lispa zarówno dla ekspertów, jak i dla poczÈt-
kujÈcych programistów Lispa.
Clojure to jÚzyk funkcyjny
Clojure jest jÚzykiem funkcyjnym, natomiast nie jest (w odróĝnieniu od Haskella)
czysto funkcyjny. Oto cechy jÚzyków funkcyjnych:
Funkcje to peïnoprawne obiekty. Oznacza to, ĝe funkcje moĝna two-
rzyÊ w czasie wykonywania programu, przekazywaÊ, zwracaÊ i ogólnie
uĝywaÊ 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 bïÚdy i znacznie ïatwiejsze do wielokrotnego uĝytku. Na przykïad
poniĝszy krótki program wyszukuje w bazie danych utwory kaĝdego kompozy-
tora, który napisaï dzieïo pod tytuïem „Requiem”:
(for [c compositions :when (= Requiem (:name c))] (:composer c))
- ( W. A. Mozart Giuseppe Verdi )
Rozdziaï 1. • Wprowadzenie
31
Sïowo for nie jest poczÈtkiem pÚtli, ale wyraĝenia listowego (ang. list compre-
hension). Kod ten oznacza „dla kaĝdego c z kolekcji compositions, gdzie nazwÈ
c jest Requiem , podaj kompozytora c”. Wyraĝenia listowe omawiamy w punkcie
„Przeksztaïcanie sekwencji”.
Przykïadowy kod ma cztery poĝÈdane cechy:
Jest prosty. Nie obejmuje pÚtli, zmiennych ani zmiennego stanu.
Jest bezpieczny ze wzglÚdu na wÈtki. Nie wymaga stosowania blokad.
Jest moĝliwy do równolegïego wykonywania. Kaĝdy krok moĝna przy-
dzieliÊ do odrÚbnego wÈtku bez koniecznoĂci modyfikowania kodu po-
szczególnych kroków.
Jest uniwersalny. KolekcjÈ compositions moĝe byÊ zwykïy 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. WiÚkszoĂÊ programów obiektowych pisze siÚ
w stylu imperatywnym, dlatego nie majÈ one ĝadnych z wymienionych wczeĂniej
zalet. SÈ niepotrzebnie skomplikowane, niebezpieczne ze wzglÚdu na wÈtki, nie
umoĝliwiajÈ równolegïego dziaïania, a kod jest trudny do uogólnienia. Bezpo-
Ărednie porównanie podejĂcia funkcyjnego i imperatywnego znajdziesz w pod-
rozdziale 2.7, „Gdzie siÚ podziaïa pÚtla for?”.
ProgramiĂci znajÈ zalety jÚzyków funkcyjnych juĝ od dïugiego czasu. Jednak
jÚzyki funkcyjne, na przykïad Haskell, nie zdobyïy dominujÈcej pozycji. Wyni-
ka to z tego, ĝe nie wszystkie operacje moĝna wygodnie wykonaÊ w podejĂciu
czysto funkcyjnym.
SÈ cztery powody, dla których jÚzyk Clojure moĝe zyskaÊ wiÚksze zaintereso-
wanie niĝ dawne jÚzyki funkcyjne:
PodejĂcie funkcyjne jest dziĂ bardziej przydatne niĝ kiedykolwiek
wczeĂniej. PojawiajÈ siÚ maszyny o coraz wiÚkszej liczbie rdzeni, a jÚ-
zyki funkcyjne pozwalajÈ ïatwo wykorzystaÊ moĝliwoĂci takiego sprzÚtu.
Programowanie funkcyjne omawiamy w rozdziale 4., „Programowanie
funkcyjne”.
W jÚzykach funkcyjnych niewygodnie zarzÈdza siÚ stanem, jeĂli musi
siÚ on zmieniaÊ. Clojure udostÚpnia mechanizmy do obsïugi zmien-
nego stanu za pomocÈ programowej pamiÚci transakcyjnej i referencji,
agentów, atomów i wiÈzania dynamicznego.
32
Programowanie w jÚzyku Clojure
W wielu jÚzykach funkcyjnych typy sÈ okreĂlane statycznie. W jÚzyku
Clojure stosuje siÚ dynamiczne okreĂlanie typów, co uïatwia zadanie
programistom poznajÈcym dopiero programowanie funkcyjne.
Wywoïywanie w jÚzyku Clojure kodu Javy nie odbywa siÚ w modelu
funkcyjnym. Przy korzystaniu z Javy wkraczamy w znany Ăwiat zmien-
nych obiektów. Zapewnia to wygodÚ poczÈtkujÈcym, którzy uczÈ siÚ
programowania funkcyjnego, a takĝe pozwala w razie potrzeby zrezy-
gnowaÊ z podejĂcia funkcyjnego. Wywoïywanie kodu Javy opisujemy
w rozdziale 9., „Sztuczki z JavÈ”.
Mechanizmy zarzÈdzania zmianÈ stanu w jÚzyku Clojure umoĝliwiajÈ pisanie
programów wspóïbieĝnych bez bezpoĂredniego korzystania z blokad i uzupeï-
niajÈ podstawowy funkcjonalny rdzeñ jÚzyka.
Clojure upraszcza programowanie wspóïbieĝne
Mechanizmy programowania funkcyjnego dostÚpne w Clojure uïatwiajÈ pisanie
kodu bezpiecznego ze wzglÚdu na wÈtki. Poniewaĝ niezmienne struktury danych
nigdy siÚ nie zmieniajÈ, nie wystÚpuje zagroĝenie uszkodzeniem danych w wy-
niku dziaïania innych wÈtków.
Jednak obsïuga wspóïbieĝnoĂci w Clojure wykracza poza mechanizmy progra-
mowania funkcyjnego. JeĂli potrzebne sÈ referencje do zmiennych danych, Clojure
zabezpiecza je za pomocÈ programowej pamiÚci transakcyjnej (ang. software
transactional memory — STM). STM sïuĝy do tworzenia kodu bezpiecznego
ze wzglÚdu na wÈtki i jest rozwiÈzaniem wyĝszego poziomu niĝ blokady z Javy.
Zamiast stosowaÊ podatne na bïÚdy strategie blokowania danych, moĝna za-
bezpieczyÊ wspóïuĝytkowany stan za pomocÈ transakcji. Jest to duĝo lepsze
podejĂcie, poniewaĝ wielu programistów dobrze zna transakcje z uwagi na do-
Ăwiadczenie w korzystaniu z baz danych.
Poniĝszy kod tworzy dziaïajÈcÈ, bezpiecznÈ ze wzglÚdu na wÈtki bazÚ danych
z kontami przechowywanÈ w pamiÚci:
(def accounts (ref #{}))
(defrecord Account [id balance])
Funkcja ref tworzy zabezpieczonÈ za pomocÈ transakcji referencjÚ do bieĝÈcego
stanu bazy danych. Aktualizowanie stanu jest niezwykle proste. Poniĝej poka-
zujemy, jak dodaÊ do bazy nowe konto:
(dosync
(alter accounts conj (- Account CLJ 1000.00)))
Rozdziaï 1. • Wprowadzenie
33
Instrukcja dosync powoduje aktualizowanie bazy accounts w transakcji. RozwiÈ-
zanie to zapewnia bezpieczeñstwo ze wzglÚdu na wÈtki i jest ïatwiejsze w sto-
sowaniu od blokad. DziÚki transakcjom nigdy nie trzeba martwiÊ siÚ o to, które
obiekty i w jakiej kolejnoĂci naleĝy zablokowaÊ. PodejĂcie transakcyjne spraw-
dza siÚ lepiej takĝe w niektórych standardowych zastosowaniach. Przykïadowo:
w modelu tym wÈtki wczytujÈce dane nigdy nie muszÈ blokowaÊ danych.
ChoÊ przedstawiony przykïad jest bardzo prosty, technika jest ogólna i spraw-
dza siÚ takĝe w praktyce. WiÚcej informacji o wspóïbieĝnoĂci i pamiÚci STM
w jÚzyku Clojure znajdziesz w rozdziale 5., „Stan”.
Wykorzystanie maszyny JVM w Clojure
Clojure zapewnia przejrzysty, prosty i bezpoĂredni dostÚp do Javy. W kodzie
moĝna bezpoĂrednio wywoïaÊ metody z dowolnego interfejsu API Javy:
(System/getProperties)
- {java.runtime.name=Java(TM) SE Runtime Environment
... i wiele innych ...
Clojure obejmuje wiele skïadniowych mechanizmów do wywoïywania kodu
w Javie. Nie omawiamy tu ich szczegóïowo (zobacz podrozdziaï 2.5, „Wywo-
ïywanie kodu w Javie”), warto jednak wspomnieÊ, ĝe w Clojure wystÚpuje mniej
kropek i mniej nawiasów niĝ w analogicznym kodzie Javy:
// Java
hello .getClass().getProtectionDomain()
; Clojure
(.. hello getClass getProtectionDomain)
Clojure udostÚpnia proste funkcje do implementowania interfejsów Javy i two-
rzenia klas pochodnych od klas Javy. Ponadto wszystkie funkcje jÚzyka Clojure
obejmujÈ implementacjÚ interfejsów Callable i Runnable. Dlatego moĝna ïatwo
przekazaÊ poniĝszÈ funkcjÚ anonimowÈ do konstruktora klasy Thread Javy:
(.start (new Thread (fn [] (println Witaj (Thread/currentThread)))))
- Witaj # Thread Thread[Thread-0,5,main]
Dziwne dane wyjĂciowe wynikajÈ ze sposobu wyĂwietlania informacji o obiek-
tach Javy w jÚzyku Clojure. Thread to nazwa klasy danego obiektu, a czïon
Thread[Thread-0,5,main] to efekt wywoïania metody toString obiektu.
(Zauwaĝ, ĝe w przedstawionym kodzie nowy wÈtek dziaïa aĝ do zakoñczenia pracy,
natomiast jego dane wyjĂciowe mogÈ w dziwny sposób przeplataÊ siÚ z wierszami
zachÚty Ărodowiska REPL. Nie jest to problem z jÚzykiem Clojure, a jedynie
wynik zapisywania danych do strumienia wyjĂcia przez wiÚcej niĝ jeden wÈtek).
34
Programowanie w jÚzyku Clojure
Poniewaĝ skïadnia do wywoïywania kodu Javy w Clojure jest przejrzysta i pro-
sta, zwykle uĝywa siÚ Javy bezpoĂrednio, zamiast ukrywaÊ kod w tym jÚzyku za
charakterystycznymi dla Lispa nakïadkami.
PoznaïeĂ juĝ kilka powodów do stosowania jÚzyka Clojure. Pora przystÈpiÊ do
pisania kodu.
1.2. Szybkie wprowadzenie
do programowania w Clojure
Aby uruchomiÊ Ărodowisko jÚzyka Clojure i kod z tej ksiÈĝki, potrzebujesz
dwóch rzeczy. Oto one:
¥rodowisko uruchomieniowe Javy. Pobierz4 i zainstaluj JavÚ w wersji
5. lub nowszej. W wersji 6. znacznie poprawiono wydajnoĂÊ i system
informowania o wyjÈtkach, dlatego warto stosowaÊ wïaĂnie jÈ.
Leiningen5. Leiningen to narzÚdzie do zarzÈdzania zaleĝnoĂciami i uru-
chamiania operacji na kodzie. Jest to takĝe najpopularniejsze w Ăwiecie
jÚzyka Clojure narzÚdzie do wykonywania tych zadañ.
Leiningen posïuĝy do zainstalowania jÚzyka Clojure i wszystkich elementów
wymaganych w przykïadowym kodzie. JeĂli masz juĝ zainstalowane narzÚdzie
Leiningen, wiesz zapewne, jak z niego korzystaÊ. Jeĝeli jeszcze nie masz po-
trzebnej wiedzy, zapoznaj siÚ z krótkim wprowadzeniem ze strony narzÚdzia
w serwisie GitHub6. Znajdziesz tam instrukcje dotyczÈce instalacji, a takĝe pod-
stawowe informacje na temat uĝytkowania Leiningena. Nie musisz jednak uczyÊ
siÚ wszystkiego, poniewaĝ w ksiÈĝce opisujemy polecenia potrzebne do uru-
chomienia przykïadów.
W trakcie pracy z ksiÈĝkÈ korzystaj z jÚzyka Clojure w wersji wïaĂciwej dla
przykïadowego kodu. Po przeczytaniu ksiÈĝki moĝesz zastosowaÊ siÚ do in-
strukcji z ramki „Samodzielne budowanie jÚzyka Clojure” i zbudowaÊ aktualnÈ
wersjÚ jÚzyka.
4 http://www.oracle.com/technetwork/java/javase/downloads/index.html
5 http://github.com/technomancy/leiningen
6 http://github.com/technomancy/leiningen
Rozdziaï 1. • Wprowadzenie
35
W punkcie „Pobieranie przykïadowego kodu”, znajdziesz instrukcje dotyczÈce
pobierania przykïadowego kodu. Po ĂciÈgniÚciu przykïadowego kodu trzeba
uĝyÊ Leiningena do pobrania zaleĝnoĂci. W katalogu gïównym z kodem wywoïaj
nastÚpujÈcÈ instrukcjÚ:
lein deps
Samodzielne budowanie jÚzyka Clojure
Moĝliwe, ĝe chcesz zbudowaÊ jÚzyk Clojure na podstawie kodu ěródïo-
wego, aby uzyskaÊ nowe funkcje i wprowadziÊ poprawki bïÚdów. Moĝna
zrobiÊ to w nastÚpujÈcy sposób:
git clone git://github.com/clojure/clojure.git
cd clojure
mvn package
Przykïadowy kod jest regularnie aktualizowany pod kÈtem nowych rozwiÈ-
zañ wprowadzanych w jÚzyku Clojure. Zapoznaj siÚ z plikiem README
w przykïadowym kodzie, aby sprawdziÊ numer najnowszej wersji, dla
której sprawdzono kod.
ZaleĝnoĂci sÈ pobierane i umieszczane w odpowiednim miejscu. Moĝesz prze-
testowaÊ zainstalowane narzÚdzia przez przejĂcie do katalogu z przykïadowym
kodem i uruchomienie Ărodowiska REPL jÚzyka Clojure. Leiningen obejmuje
skrypt uruchomieniowy Ărodowiska REPL, który wczytuje jÚzyk Clojure wraz
z zaleĝnoĂciami potrzebnymi w dalszych rozdziaïach.
lein repl
Po udanym uruchomieniu Ărodowiska REPL powinien pojawiÊ siÚ wiersz
zachÚty z tekstem user= :
Clojure
user=
Teraz jesteĂ gotowy do wyĂwietlenia tekstu „Witaj, Ăwiecie”.
Korzystanie ze Ărodowiska REPL
Aby pokazaÊ, jak korzystaÊ ze Ărodowiska REPL, tworzymy kilka wersji kodu
wyĂwietlajÈcego tekst „Witaj, Ăwiecie”. Najpierw wpisz kod (println Witaj,
Ăwiecie ) w wierszu zachÚty Ărodowiska REPL.
user= (println Witaj, Ăwiecie )
- Witaj, Ăwiecie
36
Programowanie w jÚzyku Clojure
Drugi wiersz, Witaj, Ăwiecie, to ĝÈdane dane wyjĂciowe z konsoli.
Teraz umieĂÊmy kod w funkcji, która potrafi „zwracaÊ siÚ” do uĝytkownika po
imieniu.
(defn hello [name] (str Witaj, name))
- # user/hello
Rozïóĝmy ten kod na fragmenty. Oto one:
defn sïuĝy do definiowania funkcji;
hello to nazwa funkcji;
funkcja hello przyjmuje jeden argument, name;
str to wywoïanie funkcji ïÈczÈcej dowolnÈ listÚ argumentów w ïañcuch
znaków;
defn, hello, name i str to symbole, czyli nazwy prowadzÈce do róĝnych
elementów; dozwolone symbole opisujemy w punkcie „Symbole”.
Przyjrzyj siÚ zwracanej wartoĂci, # user/hello. Przedrostek # oznacza, ĝe
funkcjÚ zapisano w zmiennej jÚzyka Clojure, a user to przestrzeñ nazw, w której
znajduje siÚ ta funkcja. Jest to domyĂlna przestrzeñ nazw w Ărodowisku REPL,
odpowiadajÈca domyĂlnemu 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 moĝna wywoïaÊ funkcjÚ hello i przekazaÊ do niej imiÚ.
user= (hello Janku )
- Witaj, Janku
JeĂli Ărodowisko REPL znajduje siÚ w dziwnym stanie, najïatwiej zamknÈÊ je
za pomocÈ kombinacji klawiszy Ctrl+C w systemie Windows lub Ctrl+D
w systemach uniksowych, a nastÚpnie ponownie uruchomiÊ.
Specjalne zmienne
¥rodowisko REPL obejmuje szereg przydatnych zmiennych specjalnych. W czasie
pracy w Ărodowisku REPL wyniki obliczania trzech ostatnich wyraĝeñ znaj-
dujÈ siÚ w specjalnych zmiennych *1, *2 i *3. Pozwala to na wygodnÈ pracÚ
w modelu iteracyjnym. Spróbujmy poïÈczyÊ kilka powitañ.
user= (hello Janku )
- Witaj, Janku
user= (hello Clojure )
- Witaj, Clojure
Rozdziaï 1. • Wprowadzenie
37
Teraz moĝna zastosowaÊ specjalne zmienne do poïÈczenia wyników ostatnich
instrukcji.
(str *1 i *2)
- Witaj, Clojure i Witaj, Janku
Popeïnienie bïÚdu w Ărodowisku REPL prowadzi do zgïoszenia wyjÈtku Javy
(z uwagi na zwiÚzïoĂÊ szczegóïy pomijamy). Niedozwolone jest na przykïad
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 wywoïañ. W specjalnej zmiennej *e znajdujÈ
siÚ informacje o ostatnim wyjÈtku. Poniewaĝ wyjÈtki w Clojure sÈ wyjÈtkami
Javy, moĝna wyĂwietliÊ stos wywoïañ za pomocÈ instrukcji pst (od ang. print
stacktrace, czyli wyĂwietl stos wywoïañ)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óïdziaïanie z JavÈ omawiamy w rozdziale 9., „Sztuczki z JavÈ”.
JeĂli blok kodu jest zbyt dïugi, aby moĝna go wygodnie wpisaÊ w Ărodowisku
REPL, umieĂÊ kod w pliku, a nastÚpnie wczytaj ten plik w Ărodowisku. Moĝesz
uĝyÊ Ăcieĝki bezwzglÚdnej lub podaÊ jÈ wzglÚdem miejsca uruchomienia Ărodo-
wiska REPL.
; Zapisz kod w pliku temp.clj, a nastÚpnie wywoïaj instrukcjÚ:
user= (load-file temp.clj )
REPL to znakomite Ărodowisko do wypróbowywania pomysïów i otrzymywania
natychmiastowych informacji zwrotnych. Aby jak najlepiej wykorzystaÊ ksiÈĝkÚ,
w trakcie jej lektury nie zamykaj Ărodowiska REPL.
7 Instrukcja pst jest dostÚpna tylko w Clojure 1.3.0 i nowszych wersjach.
38
Programowanie w jÚzyku Clojure
Dodawanie stanu wspóïuĝytkowanego
Funkcja hello z poprzedniego przykïadu to czysta funkcja, czyli taka, której
dziaïanie nie ma efektów ubocznych. Czyste funkcje ïatwo siÚ pisze i testuje.
SÈ takĝe ïatwe do zrozumienia, dlatego w wielu sytuacjach warto je stosowaÊ.
Jednak w wiÚkszoĂci programów wystÚpuje wspóïuĝytkowany stan, a do zarzÈ-
dzania nim sïuĝÈ funkcje typu impure (czyli takie, które nie sÈ czyste). Dodajmy
do funkcji hello mechanizm Ăledzenia liczby uĝytkowników. Najpierw trzeba
utworzyÊ strukturÚ danych do przechowywania tej liczby. Uĝyjmy do tego zbioru.
#{}
- #{}
#{} to literaï oznaczajÈcy pusty zbiór. Potrzebna jest teĝ operacja conj.
(conj coll item)
Instrukcja conj (od ang. conjoin, czyli ïÈczyÊ) tworzy nowÈ kolekcjÚ z dodawanym
elementem. DoïÈczmy element do zbioru, aby upewniÊ siÚ, ĝe powstaje nowy zbiór.
(conj #{} Janku )
- #{ Janku }
Tworzenie nowych zbiorów jest juĝ moĝliwe. Pora opracowaÊ sposób na spraw-
dzanie aktualnego zbioru uĝytkowników. Clojure udostÚpnia kilka sïuĝÈcych do
tego typów referencyjnych. Najprostszym typem referencyjnym jest atom.
(atom initial-state)
Aby okreĂliÊ nazwÚ atomu, naleĝy uĝyÊ instrukcji def.
(def symbol initial-value?)
Instrukcja def przypomina polecenie defn, ale jest ogólniejsza. Przy jej uĝyciu
moĝna definiowaÊ funkcje lub dane. Uĝyjmy sïowa atom do utworzenia atomu
i instrukcji def do powiÈzania atomu z nazwÈ visitors.
(def visitors (atom #{}))
- # user/visitors
Aby zaktualizowaÊ referencjÚ, trzeba uĝyÊ funkcji, na przykïad swap!.
(swap! r update-fn args)
Funkcja swap! przeprowadza operacjÚ update-fn na referencji r i w razie potrzeby
uĝywa przy tym opcjonalnych argumentów args. Spróbujmy wstawiÊ uĝytkow-
nika do kolekcji visitors, uĝywajÈc do aktualizowania funkcji conj.
(swap! visitors conj Janku )
- #{ Janku }
Rozdziaï 1. • Wprowadzenie
39
atom to tylko jeden z kilku typów referencyjnych dostÚpnych w jÚzyku Clojure.
Wybór odpowiedniego typu referencyjnego nie jest prosty (zagadnienie to
omawiamy w rozdziale 5., „Stan”).
W dowolnym momencie moĝna sprawdziÊ zawartoĂÊ pamiÚci, do której prowadzi
referencja. Sïuĝy do tego instrukcja deref lub jej krótszy odpowiednik, @.
(deref visitors)
- #{ Janku }
@visitors
- #{ Janku }
Teraz moĝna zbudowaÊ nowÈ, bardziej rozbudowanÈ wersjÚ funkcji hello.
src/examples/introduction.clj
(defn hello
WyĂwietla powitanie na wyjĂciu, uĝywajÈc nazwy uĝytkownika.
Potrafi stwierdziÊ, ĝe korzystaïeĂ juĝ z programu.
[username]
(swap! visitors conj username)
(str Witaj, username))
Teraz sprawděmy, czy uĝytkownicy sÈ poprawnie zapisywani w pamiÚci.
(hello Marku )
- Witaj, Marku
@visitors
- #{ Jacku Janku Marku }
Na Twoim komputerze lista uĝytkowników bÚdzie prawdopodobnie inna. Na tym
wïaĂnie polega problem ze stanem. Efekty sÈ róĝne w zaleĝnoĂci od tego, kiedy
zaszïy dane zdarzenia. ZrozumieÊ funkcjÚ moĝna na podstawie jej analizy.
Aby zrozumieÊ stan, trzeba poznaÊ caïÈ historiÚ dziaïania programu.
JeĂli to moĝliwe, unikaj przechowywania stanu. Jeĝeli jest to niemoĝliwe, dbaj
o to, aby stanem moĝna byïo zarzÈdzaÊ. Uĝywaj do tego typów referencyjnych,
na przykïad atomów. Atomy (i wszystkie inne typy referencyjne w Clojure) sÈ
bezpieczne przy korzystaniu z wielu wÈtków i procesorów. Co lepsze, zapew-
nienie bezpieczeñstwa nie wymaga stosowania blokad, które bywajÈ skompli-
kowane w uĝyciu.
Na tym etapie powinieneĂ umieÊ juĝ wprowadzaÊ krótkie fragmenty kodu
w Ărodowisku REPL. Wprowadzanie wiÚkszych porcji kodu odbywa siÚ podob-
nie. Takĝe biblioteki jÚzyka Clojure moĝna wczytywaÊ i uruchamiaÊ z poziomu
Ărodowiska REPL. Dalej pokazujemy, jak to zrobiÊ.
40
Programowanie w jÚzyku Clojure
1.3. Biblioteki jÚzyka Clojure
Kod jÚzyka Clojure jest umieszczony w bibliotekach. Kaĝda biblioteka jÚzyka
Clojure znajduje siÚ w przestrzeni nazw, która jest odpowiednikiem pakietu
Javy. BibliotekÚ jÚzyka Clojure moĝna wczytaÊ za pomocÈ instrukcji require.
(require quoted-namespace-symbol)
JeĂli programista ĝÈda biblioteki o nazwie clojure.java.io, Clojure szuka pliku
o nazwie clojure/java/io.clj w Ăcieĝce ze zmiennej CLASSPATH. Zobaczmy, jaki jest
tego efekt.
user= (require clojure.java.io)
- nil
PoczÈtkowy pojedynczy apostrof ( ) jest niezbÚdny i sïuĝy do dosïownego 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 moĝesz wczytaÊ przykïadowy kod do tego rozdziaïu
(bibliotekÚ examples.introduction).
user= (require examples.introduction)
- nil
Biblioteka examples.introduction obejmuje implementacjÚ generowania liczb
Fibonacciego. W jÚzykach funkcyjnych jest to tradycyjny program typu „Witaj,
Ăwiecie”. Liczby Fibonacciego omawiamy szczegóïowo w podrozdziale 4.2,
„»Leniwe« podejĂcie”. Na razie upewnij siÚ, ĝe moĝesz uruchomiÊ przykïadowÈ
funkcjÚ fibs. Wprowadě poniĝszy 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)
JeĂli otrzymaïeĂ 10 pierwszych liczb Fibonacciego (wymienionych powyĝej),
poprawnie zainstalowaïeĂ przykïadowy kod z ksiÈĝki.
Wszystkie przykïady sprawdziliĂmy za pomocÈ testów jednostkowych (testy
znajdujÈ siÚ w katalogu examples/test). Samych testów nie omawiamy w ksiÈĝce,
jednak mogÈ okazaÊ siÚ przydatnym ěródïem wiedzy. Aby uruchomiÊ testy jed-
nostkowe, uĝyj instrukcji lein test.
Rozdziaï 1. • Wprowadzenie
41
Instrukcje require i use
Po zaĝÈdaniu biblioteki jÚzyka Clojure za pomocÈ instrukcji require elementy
z biblioteki trzeba wskazywaÊ za pomocÈ peïnej nazwy. Zamiast nazwy fibs
trzeba uĝyÊ okreĂlenia examples.introduction/fibs. Uruchom drugi egzemplarz
Ărodowiska REPL8 i wprowadě poniĝszy kod.
(require examples.introduction)
- nil
(take 10 examples.introduction/fibs)
- (0 1 1 2 3 5 8 13 21 34)
Wprowadzanie peïnych nazw szybko staje siÚ kïopotliwe. Moĝesz uĝyÊ instrukcji
refer dla przestrzeni nazw i odwzorowaÊ wszystkie nazwy z tej przestrzeni na
bieĝÈcÈ przestrzeñ nazw.
(refer quoted-namespace-symbol)
Wywoïaj instrukcjÚ refer dla przestrzeni examples.introduction i sprawdě, czy
moĝesz bezpoĂrednio wywoïaÊ 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ě nastÚpujÈce instrukcje.
(use examples.introduction)
- nil
(take 10 fibs)
- (0 1 1 2 3 5 8 13 21 34)
W trakcie pracy z przykïadowym kodem z ksiÈĝki moĝesz wywoïaÊ instrukcjÚ
require lub use z opcjÈ :reload, aby wymusiÊ ponowne wczytanie biblioteki.
(use :reload examples.introduction)
- nil
Opcja :reload jest przydatna, jeĂli wprowadzasz zmiany i chcesz sprawdziÊ ich
efekt bez ponownego uruchamiania Ărodowiska REPL.
8 Otwarcie nowego Ărodowiska REPL zapobiega konfliktom nazw miÚdzy utworzonym
wczeĂniej kodem a funkcjami o tych samych nazwach z przykïadowego kodu. W praktyce
nie stanowi to problemu. Zagadnienie to omawiamy w punkcie „Przestrzenie nazw”.
42
Programowanie w jÚzyku Clojure
Znajdowanie dokumentacji
Potrzebna dokumentacja czÚsto jest dostÚpna bezpoĂrednio w Ărodowisku REPL.
NajprostszÈ funkcjÈ pomocniczÈ9 jest doc.
(doc name)
Uĝyjmy funkcji doc do wyĂwietlenia 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 wyjĂciowych funkcji doc obejmuje peïnÈ nazwÚ spraw-
dzanej funkcji. W drugim znajdujÈ siÚ argumenty generowane bezpoĂrednio
w kodzie. Wybrane czÚsto stosowane nazwy argumentów i ich zastosowanie
omawiamy w ramce „Zwyczajowe nazwy parametrów”. Dalsze wiersze obejmujÈ
ïañcuch znaków dokumentacji, jeĂli jest on podany w definicji funkcji.
Zastosowanie
Tablica Javy
Agent
Kolekcja
Wyraĝenie
Funkcja
Indeks
Referencja
Wektor
WartoĂÊ
Zwyczajowe nazwy parametrów
añcuchy 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
a
agt
coll
expr
f
idx
r
v
val
Nazwy mogÈ wydawaÊ siÚ krótkie, jednak jest tak nie bez powodu —
„dobre” nazwy czÚsto sÈ juĝ „zajÚte” przez funkcje jÚzyka Clojure! Uĝy-
wanie dla parametrów nazw identycznych z nazwami funkcji jest dopusz-
czalne, ale uznaje siÚ to za oznakÚ zïego stylu. Parametr zasïania wtedy
funkcjÚ, dlatego jest ona niedostÚpna, kiedy parametr znajduje siÚ w za-
siÚgu programu. Dlatego nie naleĝy nazywaÊ referencji ref, agentów
agent, a liczników — count, poniewaĝ sÈ to nazwy funkcji.
9 Tak naprawdÚ doc to makro jÚzyka Clojure.
Rozdziaï 1. • Wprowadzenie
43
añcuch znaków dokumentacji moĝna dodaÊ do funkcji przez umieszczenie go
bezpoĂrednio po jej nazwie.
src/examples/introduction.clj
(defn hello
WyĂwietla powitanie na wyjĂciu, uĝywajÈc nazwy uĝytkownika.
[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 wyraĝenia regularnego lub ïañcucha
znaków.
(find-doc s)
Za pomocÈ funkcji find-doc moĝna sprawdziÊ, w jaki sposób Clojure skraca
kolekcje.
user= (find-doc reduce )
-------------------------
clojure/areduce
([a idx ret init expr])
Macro
... Szczegóïy pominiÚto ...
-------------------------
clojure/reduce
([f coll] [f val coll])
... Szczegóïy pominiÚto ...
Funkcja reduce pozwala w skrócony sposób stosowaÊ operacje do kolekcji
jÚzyka Clojure. Omawiamy jÈ w punkcie „Przeksztaïcanie sekwencji”. Funkcja
areduce wspóïdziaïa z tablicami Javy, a opisujemy jÈ w punkcie „Korzystanie
z kolekcji Javy”.
Duĝa czÚĂÊ jÚzyka Clojure jest napisana w nim samym, dlatego lektura jego
kodu ěródïowego to pouczajÈce zadanie. Kod ěródïowy funkcji jÚzyka Clojure
moĝna wyĂwietliÊ za pomocÈ instrukcji source z biblioteki repl.
(clojure.repl/source symbol)
WyĂwietlmy kod ěródïowy prostej funkcji identity.
(use clojure.repl)
(source identity)
- (defn identity
Returns its argument.
{:added 1.0
:static true}
[x] x)
44
Programowanie w jÚzyku Clojure
OczywiĂcie, moĝna teĝ uĝywaÊ interfejsu API Reflection Javy. Metody class,
ancestors, instance? i podobne pozwalajÈ sprawdziÊ model obiektowy Javy
i informujÈ na przykïad o tym, ĝe kolekcje jÚzyka Clojure sÈ jednoczeĂnie 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 jÚzyka Clojure znajdziesz na stronie
http://clojure.github.com/clojure. W ramce widocznej w prawej czÚĂci tej strony
znajdujÈ siÚ odnoĂniki do wszystkich funkcji i makr. Po lewej stronie umiesz-
czono odnoĂniki do artykuïów na temat róĝnych cech jÚzyka Clojure.
1.4. Podsumowanie
WïaĂnie zakoñczyïeĂ szybki przeglÈd jÚzyka Clojure. PoznaïeĂ dajÈcÈ duĝe
moĝliwoĂci skïadniÚ tego jÚzyka i jego zwiÈzki z Lispem, a takĝe zobaczyïeĂ, jak
ïatwe jest wywoïywanie w Clojure kodu Javy.
UruchomiïeĂ jÚzyk Clojure w swoim Ărodowisku, a takĝe napisaïeĂ w Ărodowisku
REPL krótkie programy ilustrujÈce programowanie funkcyjne i sïuĝÈcy do obsïugi
stanu model referencji. Pora przyjrzeÊ siÚ caïemu jÚzykowi.
Skorowidz
A
agenty, 158
bieĝÈca wartoĂÊ, 159
sprawdzanie poprawnoĂci, 160
transakcje, 161
tworzenie, 158
wykrywanie bïÚdów, 160
algebra relacji, 110
Apache Ant, 244
atomy, 157
dereferencja, 157
tworzenie, 157
ustawienie wartoĂci, 157
B
biblioteki, 20, 40
dosïowne podawanie nazwy, 40
drzewo wïaĂciwoĂci systemowych, 238
znajdowanie dokumentacji, 42
C
Clojure, 15, 23
aktualizacja, 261
aspekty, 23
biblioteki, 20, 40
drzewo wïaĂciwoĂci systemowych, 238
inspector, 238
Lazytest, 286
math.combinatorics, 275
Midje, 286
sekwencje, 87
test, 239
test.generative, 278
cechy jÚzyka, 24, 209
czytnik, 46
makra odczytu, 55
edytory kodów, 297
funkcje, 56
czyste, 38
dodawanie sugestii typów, 253
impure, 38
wywoïanie, 56
wyĝszego rzÚdu, 25
Java, 15, 33, 243
dostÚp, 243
interfejsy, 183, 255
javadoc, 70
kompilacja AOT, 248
konstrukcja new, 68
korzystanie z kolekcji, 258
mechanizm wywoïañ zwrotnych, 256
parsery SAX, 255
Ărodowisko uruchomieniowe, 34
tworzenie klas, 255
302
Programowanie w jÚzyku Clojure
Clojure
Java
tworzenie poĂredników, 255
tworzenie tablic, 258
wykorzystanie moĝliwoĂci, 247
wywoïywanie kodu, 68
wywoïywanie metody, 69
jÚzyk funkcyjny, 30
cechy, 30
konstrukcje skïadniowe, 24, 46
Leiningen, 34, 261
liczby caïkowite, 248
operatory, 248
Lisp, 15, 24, 27
makra, 28, 201
taksonomia, 216
maszyny JVM, 29
metadane, 77
model czasu, 24
niezmienne struktury danych, 27
obsïuga wyjÈtków, 244
kontrolowane wyjÈtki, 245
porzÈdkowanie zasobów, 245
reagowanie na wyjÈtki, 247
optymalizowanie wydajnoĂci, 250
dodawanie sugestii typów, 253
uĝywanie typów prostych, 250
pobieranie zaleĝnoĂci, 261
poïÈczenie z witrynÈ, 261
programowanie, 16
funkcyjne, 18, 24, 115
pÚtle, 74
reguïy, 120
wspóïbieĝne, 32
protokoïy, 24, 179, 184
przestrzeñ nazw, 36, 61, 65
rejestrowanie informacji, 264
rekordy, 193
rozkïadanie struktury, 63
moĝliwoĂci, 65
sekwencje, 29, 81
biblioteka, 87
cechy, 83
Java, 98
manipulowanie, 197
wymuszanie realizacji, 97
wyraĝenia listowe, 95
stan, 147
model aktualizacji, 163
model funkcyjny, 149
model referencyjny, 149
sterowanie przepïywem, 70
instrukcje, 70
makra, 71
rekurencja, 72
Ărodowisko REPL, 35
zmienne specjalne, 36
toĝsamoĂÊ, 147
typy referencyjne, 147
tworzenie aplikacji, 269
gra Clojurebreaker, 270
typy danych, 179, 188
cechy, 188
typy referencyjne, 38
atom, 38
wartoĂÊ, 147
wiÈzania, 61
wielometody, 225
wspóïbieĝnoĂÊ, 18, 32, 148
powody stosowania, 148
sytuacja wyĂcigu, 149
zakleszczenie, 149
zmienne, 36, 61
cechy, 62
D
definicja pustego ïañcucha znaków, 26
duck typing, 250
F
funkcje, 56
anonimowe, 58
konstrukcja, 59
powody tworzenia, 58
stosowanie, 61
unikanie, 223
Skorowidz
303
bez sprawdzania przepeïnienia, 251
czÚĂciowe wywoïanie, 134
czyste, 38, 116
niezmienne dane, 116
dodawanie sugestii typów, 253
efekty uboczne, 71
funkcje wyĝszego rzÚdu, 25
leniwe, 127
liczba argumentów, 57
listy, 105
ïañcuchy znaków dokumentacji, 42
odwzorowania, 53, 106
tworzenie, 108
operatory matematyczne, 47
predykaty, 52, 56
przejrzyste referencyjnie, 118
memoizacja, 119
rozwiniÚcie funkcji, 135
implementacja, 135
sekwencje, 83
filtrowanie, 91
predykaty, 92
przeksztaïcanie, 93
tworzenie, 88
wyraĝenia regularne, 100
sïowa kluczowe, 54
wektory, 105
wiÈzania, 62
zasiÚg leksykalny, 63
wywoïanie, 47, 56
notacja przedrostkowa, 47
notacja wrostkowa, 47
zbiory, 109
zïoĝone, 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
304
Programowanie w jÚzyku 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
obsïuga, 245
obsïuga wyjÈtków, 244
kontrolowane wyjÈtki, 244
porzÈdkowanie zasobów, 245
sekwencje, 98
kolekcje sekwencyjne, 98
wywoïywanie 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
wyĂwietlanie wÚĝa i jabïka, 175
model funkcyjny, 169
add-points, 170, 171
assoc, 171
cons, 171
dodawanie punktów, 170
eats?, 172
Skorowidz
305
head-overlaps-body?, 172
lose?, 172
move, 171
point-to-screen-rect, 171
ruch wÚĝa, 171
sprawdzanie warunków zwyciÚstwa, 172
turn, 173
tworzenie nowego jabïka, 171
tworzenie wÚĝa, 171
tworzenie zestawu staïych, 169
win?, 172
wykrywanie zetkniÚcia, 172
zjadanie jabïka, 172
zmiana kierunku wÚĝa, 173
model zmiennego stanu, 169, 173
reset-game, 173
update-direction, 174
update-positions, 174
wydïuĝanie wÚĝa, 174
zmiany pozycji wÚĝa i jabïka, 173
konstrukcje skïadniowe, 24
liczba, 46
BigDecimal, 48
BigInt, 48
caïkowita, 48
Ratio, 48
wektor, 47
zmiennoprzecinkowa, 48
lista, 46, 47
wywoïywanie funkcji, 47
ïañcuch znaków, 46, 49
wywoïanie, 50
odwzorowania, 46, 52
sekwencje, 85
rekordy, 52
wywoïywanie, 54
sïowo kluczowe, 46, 53
symbol, 46, 49
wartoĂÊ logiczna, 46
reguïy dziaïania, 51
wartoĂÊ nil, 46
wektory, 46
sekwencje, 84
zbiory, 46
sekwencje, 85
znak, 46
L
Leiningen, 34, 261
pobieranie zaleĝnoĂci, 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
jÚzyk szablonów, 210
306
Programowanie w jÚzyku Clojure
makra
konstrukcje specjalne, 215
lazy-seq, 125
let, 221
letfn, 124
manipulowanie listÈ, 210
ns, 67
obsïuga kilku konstrukcji, 208
obsïuga szablonów, 211
przechwytywanie symboli, 213
splicing unquote, 211
unquote, 211
przetwarzanie, 203
reguïy 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
wartoĂciowanie argumentów, 203, 206
nakïadki, 221
odraczanie, 220
warunkowe, 216
when, 208
when-not, 202, 208
with-open, 221, 245
with-out-str, 221
wspóïdziaïanie 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 wyraĝenia 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 podejĂcie, 118, 121
leniwe sekwencje, 121, 125
problemy rekurencyjne, 138
prosta rekurencja, 122
Skorowidz
reguïy, 120
rekurencja, 118
rekurencja koñcowa, 123
rekurencja wzajemna, 136
trwaïe struktury danych, 117
wielokrotne wykorzystanie kodu, 119
wspóïuĝytkowanie struktur, 117
wyraĝenie listowe, 31
zalety, 119
imperatywne, 31
kod pÚtli, 74
prawidïowy proces pisania kodu, 273
sekwencyjne, 97
testowanie kodu, 274
BDD, 286
generowanie danych testu, 279
podstawowe etapy, 278
programowe sprawdzanie poprawnoĂci,
280
przeprowadzanie testów, 276, 283
sprawdzanie poprawnoĂci danych
wyjĂciowych, 277
sprawdzanie poprawnoĂci kodu, 278
TDD, 286
testy jednostkowe, 286
testy regresji, 277
tworzenie danych wejĂciowych, 275
zgïaszanie bïÚdu, 284
wspóïbieĝne, 148
powody stosowania, 148
wykorzystanie abstrakcji, 180
obsïuga dodatkowych typów, 181
odczyt, 180
zapis, 180
protokoïy, 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
wiÈzania, 65
307
R
referencje, 150
sprawdzanie poprawnoĂci, 156
transakcje, 150
ACID, 151
aktualizowanie informacji, 152
atomowe, 151
izolowane, 151
licznik, 155
pamiÚÊ STM, 151
skoordynowane, 151
spójne, 151
trwaïe, 151
wartoĂÊ wewnÈtrztransakcyjna, 153
wïaĂciwoĂci, 151
zmiana encji, 150
tworzenie, 150
rekordy, 193
dodawanie metod, 195
dostÚp do pól, 194
implementacja protokoïu, 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 koñcowa, 123
optymalizacja TCO, 123
rekurencja wzajemna, 136
memoizacja, 144
optymalizowanie, 139
przeksztaïcanie na autorekurencjÚ, 138
zastÚpowanie leniwym podejĂciem, 141
308
Programowanie w jÚzyku 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
przeksztaïcanie, 93
bieĝÈca wartoĂÊ, 159
sprawdzanie poprawnoĂci, 160
transakcje, 161
tworzenie, 158
wykrywanie bïÚdów, 160
atomy, 147, 157
dereferencja, 157
tworzenie, 157
ustawienie wartoĂci, 157
model aktualizacji, 163
modele zarzÈdzania, 168
funkcje, 93
strumienie, 102
system plików, 101
funkcje, 101
tryb leniwy, 86, 96
stosowanie, 97
zalety, 96
tworze
Pobierz darmowy fragment (pdf)