Obsah
1. Programovací jazyk Clojure 14: základy práce se systémem maker
2. Smyčka REPL (Read-Eval-Print-Loop) a systém maker v tradičních LISPech
3. První písmeno ve zkratce REPL: objekt Reader a jeho makra
4. Makra „comment“, „character“ a „metadata“
6. Makra „quote“ a „syntax-quote“
7. Makra „unquote“ a „unquote-splicing“
1. Programovací jazyk Clojure 14: základy práce se systémem maker
V dnešní části seriálu o programovacím jazyku Java i o vlastnostech virtuálního stroje Javy se opět budeme zabývat popisem zajímavých vlastností programovacího jazyka Clojure, jehož původní implementace je postavená právě nad virtuálním strojem Javy (JVM). Dnes si řekneme základní informace o tom, jak lze v Clojure používat makra, která mohou velmi významným způsobem ovlivnit způsob tvorby programů, protože právě díky použití maker lze v Clojure vytvářet takzvané doménově orientované jazyky – například se může jedna o obdobu jazyka SQL atd. Již při popisu základních principů, na nichž je postaven programovací jazyk Clojure jsme si řekli, že je tento jazyk vybaven interpretrem kombinovaným s „utajeným“ překladačem zapisovaných funkcí a forem do bajtkódu virtuálního stroje Javy (všechny vyhodnocované formy jsou tedy nejprve interně přeloženy do bajtkódu a teprve poté je spuštěno jejich vyhodnocení).
Základní vlastnosti samotného interpretu jazyka Clojure jsou přitom odvozeny od interpretrů používaných ve většině variant programovacího jazyka LISP, což znamená, že autoři Clojure (resp. přesněji řečeno především jeho původní a dodnes nejaktivnější autor Rich Hickley) vychází z ověřených technologií, které byly poprvé implementovány již před více než padesáti roky v rámci vývoje LISPu a na něj navazujících jazyků (Scheme). Základem interpretru programovacího jazyka Clojure je, stejně jako v LISPu, smyčka nazývaná REPL (Read-Evaluate-Print-Loop), jejíž název vyplývá z toho, že mohla být relativně jednoduše implementována způsobem ukázaným pod tímto odstavcem. Ostatně z historického pohledu je zajímavé, že nějak podobně vlastně LISP vznikl, když si jeho autor (John McCarthy) uvědomil, že na základě implementace rekurzivní podoby funkce eval a několika dalších pomocných funkcí dokáže vytvořit plnohodnotný programovací jazyk:
(loop (print (eval (read))))
Poznámka: v programovacím jazyku Clojure by ve skutečnosti musel být tento „ortodoxní“ zápis proveden poněkud odlišným způsobem, protože zde se speciální forma loop musí zapisovat společně se speciální formou recur – loop totiž v Clojure slouží především pro označení bodu, kde začíná rekurze (a navíc i pro určení lokálních proměnných používaných uvnitř opakujícího se bloku kódu). Taktéž je nutné nahradit funkci print za println, takže v Clojure může implementace smyčky REPL vypadat například takto:
user=> (loop [] (println (eval (read))) (recur)) (+ 1 2) 3 (* 6 7) 42 (str "Hello world") Hello world ^D user=>
2. Smyčka REPL (Read-Eval-Print-Loop) a systém maker v tradičních LISPech
Jednoduchý interpret LISPu tedy mohl být teoreticky implementován pouze s využitím trojice funkcí read (načtení výrazu/formy ze standardního vstupu), print (tisk výsledku vyhodnocení výrazu/formy na standardní výstup), eval (většinou rekurzivně implementovaná funkce určená pro vyhodnocení načtené formy), které byly doplněny speciální formou či makrem loop (nekonečná smyčka – při striktním pohledu se v tomto případě nemůže jednat o funkci). Ve skutečnosti je však samozřejmě nutné, aby byl prakticky použitelný programovací jazyk doplněn o alespoň minimální množství základních funkcí a speciálních forem. V případě původního LISPu se jednalo o sedm funkcí a dvě speciální formy: atom, car, cdr, cond, cons, eq, quote, lambda a label.
Původně relativně velmi jednoduše a přitom elegantně implementovaný interpret programovacího jazyka LISP se postupně začal vyvíjet a jednou z nových a přitom mocných technik, které do něj byly přidány, jsou takzvaná makra, která se však v mnoha ohledech liší od maker používaných například v programovacích jazycích C a C++. Zatímco v céčku jsou makra zpracovávána poměrně „hloupým“ preprocesorem, který dokáže provádět textové substituce, načítat vkládané soubory a vyhodnocovat jednoduché podmínky, mohou makra implementovaná v jazyce LISP pracovat přímo se zadávanými formami, které makra mohou různým způsobem modifikovat – přitom se zde využívá faktu, že v LISPu a tudíž i v Clojure jsou programy reprezentovány ve formě (obvykle rekurzivně vnořených) seznamů, a změnou obsahu těchto seznamů lze vlastně přímo manipulovat s takzvaným abstraktním syntaktickým stromem (AST – Abstract Syntax Tree).
Není bez zajímavosti, že s AST se v LISP/Clojure může manipulovat za použití stejných mechanismů (funkcí/forem/maker), které se používají i při běžném programování – jinými slovy to znamená, že jazyk maker je stále jazykem, v němž se zapisují programy (na rozdíl od zmíněného céčka a C++, kde je jazyk maker zcela odlišný). Jinými slovy to znamená, že se při tvorbě maker musíme seznámit pouze se způsobem zápisu maker, ale v samotných makrech se mohou používat funkce, které jsme si již v tomto seriálu popsali – většinou se bude jednat o funkce pro práci se seznamy, což je vzhledem ke způsobu reprezentace programů (jako do sebe vnořených seznamů) pochopitelné.
3. První písmeno ve zkratce REPL: objekt Reader a jeho makra
Před popisem systému maker v programovacím jazyku Clojure si ještě musíme říci, že ve skutečnosti existují dva typy maker – takzvaná reader macros, neboli makra zabudovaná přímo do objektu/modulu, který načítá formy ze standardního vstupu a potom běžná makra. Nejprve se budeme zabývat makry používanými při načítání forem ze standardního vstupu. Důvodů, proč je lepší začít s popisem této skupiny maker je více, například fakt, že tato makra nelze vytvářet a existující makra nelze modifikovat (na rozdíl od Common Lispu, kde to možné je) a taktéž to, že se tato makra používají v prakticky všech zdrojových kódech, aniž by si vývojáři většinou uvědomovali, že ve svých programech nějaká makra používají :-) Důvod existence reader maker je jednoduchý – umožňují zkrácení zápisu programů, zajišťují možnost zápisu komentářů (ty totiž nejsou považovány za běžné formy, protože nevrací žádnou hodnotu, ani nil) a taktéž je možné s pomocí tohoto typu maker přidávat k symbolům, seznamům, vektorům, mapám atd. takzvaná metadata.
Zjednodušeně řečeno je možné říci, že reader makra pracují podobně jako preprocesor v programovacích jazycích C a C++, protože text zapisovaný či posílaný na standardní vstup je nejprve těmito makry zpracován a posléze je – stále v textové podobě – poslán funkci read. Tato makra tedy slouží pro provádění „pouhých“ textových substitucí a nikoli k modifikaci AST, jak je tomu u běžných maker. Která reader makra jsou v Clojure podporována, nám prozradí následující tabulka, z níž je patrné, že některá makra provádí skutečně značně jednoduchou činnost, například pouhé odstranění komentářů:
# | Makro | Název | Kapitola | Význam |
---|---|---|---|---|
1 | ; | comment | 4 | umožňuje obejít zápis (comment nějaký text) u komentářů |
2 | \ | character | 4 | používané při zápisu znakových literálů |
3 | ^ | metadata | 4 | přidání metadat k symbolům, seznamům, vektorům, mapám a množinám |
4 | ' | quote | 6 | nahrazuje zápis (quote …) |
5 | ` | syntax-quote | 6 | provádí plnou kvalifikaci symbolů + zde lze použít makra ~ a ~@ |
6 | ~ | unquote | 7 | zajistí, že se vyhodnotí pouze označená část formy (= provede substituci této části výsledkem) |
7 | ~@ | unquote-splicing | 7 | podobné předchozími makru, ovšem výsledná sekvence se vloží ve formě samostatných prvků do „obalující“ sekvence |
8 | @ | deref | 5 | nahrazuje zápis (deref …) |
9 | # | dispatch | 8 | má různé funkce: donutí reader, aby použil makro z jiné tabulky maker |
Význam všech maker zmíněných v předchozí tabulce si podrobněji vysvětlíme v navazujících kapitolách.
4. Makra „comment“, „character“ a „metadata“
S popisem maker vestavěných do objektu Reader začneme jen pozvolna, napřed si totiž popíšeme ta nejjednodušší makra, která jsme si navíc již ukázali v předchozích částech tohoto seriálu. Nejjednodušším reader makrem je s velkou pravděpodobností makro nazvané „comment“ zapisované pomocí znaku ; (středník). Veškerý text, který je zapsaný mezi středníkem a koncem řádku je ignorován, takže toto makro lze použít pro jednoduchý zápis jednořádkových komentářů. Pokud by toto reader makro neexistovalo, muselo by se pro zápis komentářů namísto toho používat normální makro comment, které se však musí zapisovat stejně, jako jakákoli jiná forma, tj. i s kulatými závorkami, což na čitelnosti komentářů určitě nepřidá. Nicméně makro comment má svou nezastupitelnou úlohu, protože pomocí něho můžeme do komentáře „uzavřít“ i delší část kódu – ostatně toto makro je použito i v samotných zdrojových kódech jazyka Clojure. Podívejme se na několik jednoduchých demonstračních příkladů:
; jednořádkové komentáře user=> ; toto je komentar user=> ; ignorovany v REPL\ user=> ; víceřádkový komentář user=> (comment komentar muze mit nekolik radku) ; povšimněte si, že (comment) vrací hodnotu nil nil user=>
Další velmi často používané reader makro se zapisuje pomocí znaku \ (zpětné lomítko). Toto makro slouží pro zápis znakových konstant (literálů) do zdrojového kódu. Tisknutelné znaky je možné zapsat buď přímo za zpětné lomítko, nebo je možné použít kód libovolného znaku z×Unicode, přičemž kód tohoto znaku musí být zapsán v hexadecimální soustavě za dvojici znaků „\u“:
; běžný tisknutelný znak: user=> \a \a ; další běžný tisknutelný znak: user=> \1 \1 ; znak "u": user=> \u \u ; znak s hexadecimální hodnotou 0x40, neboli 64: user=> \u0040 \@ ; pokus o zápis neplatného znakového literálu: user=> \aa RuntimeException Unsupported character: \aa clojure.lang.Util.runtimeException (Util.java:170) user=>
Posledním „jednoduchým“ reader makrem je makro zapisované pomocí znaku ^. Toto makro slouží k přiřazení metadat k symbolu, seznamu, vektoru, množině či mapě. S metadaty jsme se již okrajově setkali v předchozí části tohoto seriálu a budeme se jim ještě věnovat příště. Dnes si pouze ukážeme základní způsob využití makra ^:
; nastavení metadat k vektoru user=> ^{:atribut1 "Hodnota1" :atribut2 "Hodnota2"} [1 2 3] [1 2 3] ; uložení vektoru a k němu přiřazeným metadatům ; do proměnné vektor user=> (def vektor ^{:atribut1 "Hodnota1" :atribut2 "Hodnota2"} [1 2 3]) #'user/vektor ; přečtení stavu proměnné vektor user=> vektor [1 2 3] ; přečtení metadat přiřazených k proměnné vektor user=> (meta vektor) {:atribut2 "Hodnota2", :atribut1 "Hodnota1"} user=>
Navíc existuje i alternativní způsob použití makra ^, který slouží pro naplnění jediného atributu s názvem :tag:
; uložení vektoru a k němu přiřazeným metadatům ; do proměnné vektor user=> (def vektor ^"xyzzy" [1 2 3]) #'user/vektor ; přečtení stavu proměnné vektor user=> vektor [1 2 3] ; přečtení metadat přiřazených k proměnné vektor user=> (meta vektor) {:tag "xyzzy"} user=>
5. Makro „deref“
S makrem objektu reader, které se nazývá „deref“, jsme se již ve skutečnosti v tomto seriálu setkali, a to dokonce na několika místech. Toto makro totiž může být použito pro přečtení hodnoty, přesněji řečeno stavu reference a používá se u mnoha typů referencí (referenčních typů):
# | Referenční typ |
---|---|
1 | @ref |
2 | @agent |
3 | @var |
4 | @atom |
5 | @delay |
6 | @future |
7 | @promise |
Chování makra @ se však u různých typů referencí liší. Zatímco @ref, @var či @atom pouze vrátí aktuální stav reference, v případě použití @future, @promise či @agent se ve skutečnosti musí počkat na dokončení výpočtu, který běží v jiném vláknu. Pro jistotu si připomeňme tabulku s funkcemi a makry použitými při práci s nejdůležitějšími referenčními typy:
Typ | Var | Ref | Atom | Agent |
---|---|---|---|---|
Vytvoření | (def name value) | (ref value) | (atom value) | (agent value) |
Nastavení hodnoty | (set! name value) | (ref-set ref value) | (reset! atom value) | × |
Aplikace funkce | × | (alter ref funkce) | (swap! atom funkce) | (send agent funkce) |
Čtení hodnoty | name | @ref | @atom | @agent |
Demonstrační příklad na použití referencí typu ref, tedy „normálních“ proměnných platných v rámci aktuálního jmenného prostoru (ve skutečnosti je s referencemi typu ref možné provádět mnoho operací, které by s běžnými proměnnými nebyly možné):
; vytvoření refu user=> (def my-ref (ref 42)) #'user/my-ref ; vytvoření refu user=> (def string-ref (ref "Hello world")) #'user/string-ref ; přečtení refu pomocí makra @ user=> @my-ref 42 ; přečtení refu pomocí makra @ user> @string-ref "Hello world" user=>
Demonstrační příklad na použití atomů a samozřejmě i makra @:
; vytvoření nového atomu user=> (def x (atom 42)) #'user/x ; globální symbol x je navázán ; na atom a nikoli na stav identity ; (=hodnotu) user=> x #<Atom@61a907: 42> ; pro získání aktuálního stavu ; je nutné použít dereferenci user=> (deref x) 42 ; namísto (deref x) se používá ; makro preprocesoru @ user=> @x 42 ; atomická změna stavu identity user=> (reset! x 10) 10 user=> (reset! x (+ 1 2 3)) 6 user=> @x 7 ; další možnost atomické změny ; stavu identity - nyní přes funkci ; aplikovanou na atom a popř. i další ; parametry user=> (swap! x + 1) 7 user=> @x 7 user=>
Příklad vytvoření agenta, poslání funkce agentovi a čekání na dokončení výpočtu právě s použitím makra @:
; vytvoření agenta user=> (def my-agent (agent 0)) #'user/my-agent ; poslání funkce agentovi (6 je druhý parametr funkce) user=> (send my-agent + 6) #<Agent@18622f3: 0> ; poslání funkce agentovi (7 je druhý parametr funkce) user=> (send my-agent * 7) #<Agent@18622f3: 6> ; dereference - získání nového stavu - pomocí makra @ user=> @my-agent 42 user=>
Další příklad s makrem @, tentokrát použitým pro objekty future:
; vytvoření objektu typu future a spuštění paralelního vlákna user=> (def future_fibonacci1 (future (fibonacci 35))) #'user/future_fibonacci1 ; vytvoření dalšího objektu typu future a spuštění paralelního vlákna user=> (def future_fibonacci2 (future (fibonacci 35))) #'user/future_fibonacci2 ; čekání na dokončení prvního paralelně běžícího výpočtu ; - zde se využívá makro @ user=> @future_fibonacci1 9227465 ; čekání na dokončení druhého paralelně běžícího výpočtu ; - zde se využívá makro @ user=> @future_fibonacci2 9227465 user=>
Poslední demonstrační příklad: makro @ a objekty typu promise:
; vytvoření objektu typu promise ; a navázání na symbol promise-test user=> (def promise-test (promise)) #'user/promise-test ; nastavení hodnoty user=> (deliver promise-test 42) #<core$promise$reify__6153@1318b: 42> ; získání nastavené hodnoty pomocí makra @ user=> @promise-test 42 user=>
6. Makra „quote“ a „syntax-quote“
Konečně se dostáváme k zajímavějším a užitečnějším makrům. Jedno z nejdůležitějších a nejčastěji používaných maker se jmenuje „quote“ a zapisuje se pomocí apostrofu. Toto makro zakazuje vyhodnocování seznamů, protože pokud by objekt reader načetl formu ve tvaru (a b c), předal by ji do funkce eval, kde by se tato forma vyhodnotila jako volání funkce a s parametry b a c. Pokud však reader načte formu '(a b c), ztransformuje ji do tvaru (quote (a b c)), přičemž quote je speciální forma zakazující vyhodnocení. Na většinu ostatních objektů kromě seznamů nemá makro „quote“ většinou žádný vliv:
; zde nemá quote žádný vliv user=> '42 42 ; zákaz vyhodnocení seznamů jako funkce user=> '(1 2 3) (1 2 3) ; zde nemá quote žádný vliv user=> '[1 2 3] [1 2 3] ; stejné jako předchozí forma user=> [1 2 3] [1 2 3] ; zákaz vyhodnocení seznamů jako funkce user=> '(* 6 7) (* 6 7) ; zde se však seznam vyhodnotí jako funkce * user=> (* 6 7) 42 user=>
Kromě makra „quote“ ještě objekt reader rozeznává poněkud komplikovanější makro nazývané „syntax-quote“, které se zapisuje pomocí zpětného apostrofu: `. Chování tohoto makra se liší podle toho, s jakým typem objektu je použito, ovšem ve všech případech se makro chová tak, aby nedocházelo k vyhodnocení jeho argumentů, popř. ani k vyhodnocení vnořených forem. V následujících příkladech dochází k jednoduchému zákazu vyhodnocení předané formy:
user=> `42 42 user=> `(1 2 3) (1 2 3) user=> `[1 2 3] [1 2 3] user=>
Dále toho makro dokáže nahradit zjednodušené jméno symbolu jeho plně kvalifikovaným jménem. Nejlépe si to opět ukážeme na několika příkladech:
user=> `seq clojure.core/seq user=> `map clojure.core/map user=> `Integer/valueOf java.lang.Integer/valueOf user=>
Zákaz vyhodnocení a současně i náhrada zjednodušeného jména symbolu na plně kvalifikované jméno se projeví tím, že se následující seznamy (a vektor na konci) nevyhodnotí jako funkce, ale například funkce * se nahradí plným jménem:
user=> `(* 6 7) (clojure.core/* 6 7) user=> `(str "Hello" "world") (clojure.core/str "Hello" "world") user=> `[* seq str xyzzy neznamy] [clojure.core/* clojure.core/seq clojure.core/str user/xyzzy user/neznamy] user=>
Toto makro se poměrně často používá při tvorbě uživatelských maker, což si příště ukážeme na demonstračních příkladech.
7. Makra „unquote“ a „unquote-splicing“
Makro nazvané „unquote“, které se zapisuje pomocí znaku ~ (tilda) dokáže vynutit vyhodnocení určité části výrazu, a to tehdy, pokud je tento výraz umístěn v makru ` (syntax-quote), nikoli však ' (quote). Nejprve si ukažme způsob zápisu tohoto makra i to, jaký má toto makro vliv na zapisované výrazy:
; makro quote zakáže vyhodnocení celého seznamu user=> '(1 2 (* 6 7) (/ 4 2)) (1 2 (* 6 7) (/ 4 2)) ; makro syntax-quote zakáže vyhodnocení taktéž a ; současně provede náhradu jmen funkcí za jejich plný tvar user=> `(1 2 (* 6 7) (/ 4 2)) (1 2 (clojure.core/* 6 7) (clojure.core// 4 2)) ; pomocí ~ vynutíme vyhodnocení podvýrazu (* 6 7) user=> `(1 2 ~(* 6 7) (/ 4 2)) (1 2 42 (clojure.core// 4 2)) ; pomocí ~ vynutíme vyhodnocení podvýrazu (/ 4 2) user=> `(1 2 (* 6 7) ~(/ 4 2)) (1 2 (clojure.core/* 6 7) 2) ; pomocí dvou ~ vynutíme vyhodnocení obou podvýrazů user=> `(1 2 ~(* 6 7) ~(/ 4 2)) (1 2 42 2) user=>
Podobným způsobem pracuje i makro ~@, ovšem to navíc ještě provádí „zplošťování seznamů“. Prozatím si chování tohoto makra ukážeme na velmi jednoduchém umělém příkladu, ovšem příště při popisu uživatelských maker uvidíme, jak může být ~@ ve skutečnosti užitečné:
; uživatelsky definovaný seznam user=> (def s '(1 2 3)) #'user/s ; makro quote zcela zakáže vyhodnocování user=> '(1 2 3 (cons s s)) (1 2 3 (cons s s)) ; makro syntax-quote taktéž, ovšem ještě nahradí ; všechny symboly jejich plnými jmény user=> `(1 2 3 (cons s s)) (1 2 3 (clojure.core/cons user/s user/s)) ; vynutíme si vyhodnocení podvýrazu (cons s s) ; který vrací ((1 2 3) 1 2 3) user=> `(1 2 3 ~(cons s s)) (1 2 3 ((1 2 3) 1 2 3)) ; dtto, ovšem seznam, jenž je výsledkem (cons s s) ; je zploštěn (jakoby je odstraněna jedna úroveň zanoření) user=> `(1 2 3 ~@(cons s s)) (1 2 3 (1 2 3) 1 2 3) user=>
8. Makro „dispatch“
Posledním typem makra používaného interně v objektu reader, které si v dnešním článku popíšeme, je makro nazvané „dispatch“ jenž je ve zdrojovém textu zapisováno pomocí znaku # (křížek, hash symbol). Pokud reader narazí ve vstupním textu na tento znak, ví, že má na základě těsně následujícího znaku vybrat určité makro z alternativní tabulky maker. To ve skutečnosti znamená, že znak # musí být následován dalším znakem se speciálním významem. O jaký znak se jedná se dozvíme v tabulce zobrazené pod tímto odstavcem:
# | Dvojice znaků | Způsob použití | Význam |
---|---|---|---|
1 | #{ | #{prvky} | zápis množiny |
2 | #" | #„regexp-pattern“ | zápis regulárního výrazu |
3 | #' | #'var | quotování proměnných |
4 | #( | #(telo funkce) | zkrácený zápis anonymní funkce |
5 | #_ | #_text | text je ignorován – alternativní způsob komentáře |
Některé zápisy uvedené v předchozí tabulce již známe, takže si je probereme pouze krátce. Asi nejjednodušší je zápis množiny pomocí makra #{, za nímž následují prvky množiny uzavřené pravou složenou závorkou. Důvod, proč je množina zapsána takto prapodivně je velmi jednoduchý – pro množiny již nezbyly k dispozici žádné vhodné závorky, protože kulaté závorky jsou již z historických důvodů vyhrazeny pro seznamy, hranaté závorky se celkem logicky použily pro zápis vektorů a složené závorky (bez # na začátku) jsou použity pro mapy, které jsou přece jen využívány častěji než množiny (ještě by sice bylo možné použít znaky < a >, to by však kolidovalo s jejich standardním významem.
Další typ makra začíná dvojicí znaků #" a končí znakem " (uvozovky). Mezi obě uvozovky se zapisují regulární výrazy, které jsou využívány některými funkcemi, například re-seq, re-find, re-groups a re-matches. Regulární výraz uvedený v makru #" je zkontrolován a následně zkompilován ihned v čase svého načítání, na rozdíl od regulárního výrazu zadaného s využitím funkce re-pattern: v tomto případě se totiž kontrola a kompilace provede až ve chvíli, kdy je tato funkce zpracována a vyhodnocena. Více podrobností o využití regulárních výrazů si řekneme v navazujících částech tohoto seriálu, zde si jen ukážeme základy práce s nimi:
; nalezení prvního podřetězce vyhovujícímu regulárnímu výrazu user=> (re-find #"[0-9]+" "123qwe456asd") "123" ; nalezení podřetězců vyhovujících zadanému regulárnímu výrazu user=> (re-seq #"[0-9]+" "123qwe456asd") ("123" "456") ; uložení výsledku do proměnné result user=> (def result (re-seq #"[0-9]+" "123qwe456asd")) #'user/result ; získání prvního podřetězce user=> (first result) "123" ; získání druhého podřetězce user=> (second result) "456" user=>
Dalším makrem objektu reader, které začíná znakem #, je makro #(. Toto makro slouží ke zkrácenému zápisu anonymní funkce. Za kulatou závorkou je přímo zapsáno tělo anonymní funkce bez explicitního uvedení jmen parametrů. Tento zápis se transformuje do volání fn ještě v preprocesoru smyčky REPL, tj. samotná smyčka REPL již „vidí“ pouze zápis speciální formy fn). Zápis vypadá následovně:
#(tělo anonymní funkce)
To, že chybí jméno funkce je pochopitelné – kdyby funkce měla jméno, už by nebyla funkcí anonymní :-) ovšem absence jmen parametrů možná může být poněkud matoucí, protože anonymní funkce bez parametrů by vlastně odpovídala vrácení konstanty (popř. nefunkcionálním „šílenostem“ typu random :-). Ve skutečnosti však i anonymní funkce vytvořená pomocí znaku # parametry mít může. Ty jsou pojmenovány v závislosti na své pozici takto: %1, %2 atd. Předpokládá se totiž, že anonymní funkce budou velmi krátké a s malým množstvím parametrů – pokud by tomu tak nebylo, nic programátorům nebrání vrátit se ke speciální formě fn.
Ukažme si nějaké příklady použití vytvoření anonymních funkcí pomocí znaku #.
Aplikace anonymní funkce vytvořené s využitím speciální formy fn:
((fn [x y] (* x y)) 6 7)
Zkrácený (ekvivalentní) způsob zápisu:
(#(* %1 %2) 6 7)
Anonymní funkce se velmi často používají jako parametry funkcí map, pmap, filter apod.:
(map #(/ 2 %1) [1 2 3 4 5 6]) (2 1 2/3 1/2 2/5 1/3)
Popř.:
(map #(/ 2 %1) (range 1 7)) (2 1 2/3 1/2 2/5 1/3)
Zbylá makra začínající křížkem si popíšeme příště.
9. Odkazy na Internetu
- Tech behind Tech: Clojure Macros Simplified
http://techbehindtech.com/2010/09/28/clojure-macros-simplified/ - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - Eulerovo číslo
http://cs.wikipedia.org/wiki/Eulerovo_číslo - List comprehension
http://en.wikipedia.org/wiki/List_comprehension - List Comprehensions in Clojure
http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html - Clojure Programming Concepts: List Comprehension
http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#List_Comprehension - Clojure core API: for macro
http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/for - cirrus machina – The Clojure for macro
http://www.cirrusmachina.com/blog/comment/the-clojure-for-macro/ - Clojure.org: Clojure home page
http://clojure.org/downloads - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - A Couple of Clojure Agent Examples
http://lethain.com/a-couple-of-clojure-agent-examples/ - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDoc
http://clojuredocs.org/ - Clojure (Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/ - JSR 223: Scripting for the JavaTM Platform
http://jcp.org/en/jsr/detail?id=223 - JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
http://jcp.org/en/jsr/detail?id=292 - Java 7: A complete invokedynamic example
http://niklasschlimm.blogspot.com/2012/02/java-7-complete-invokedynamic-example.html - InvokeDynamic: Actually Useful?
http://blog.headius.com/2007/01/invokedynamic-actually-useful.html - A First Taste of InvokeDynamic
http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html - Java 6 try/finally compilation without jsr/ret
http://cliffhacks.blogspot.com/2008/02/java-6-tryfinally-compilation-without.html - An empirical study of Java bytecode programs
http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/ - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/ - Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
http://www.root.cz/clanky/jamvm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - BCEL Home page
http://commons.apache.org/bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - FindBugs
http://findbugs.sourceforge.net/ - GNU Classpath
www.gnu.org/s/classpath/ - Java VMs Compared
http://bugblogger.com/java-vms-compared-160/ - JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - Java performance
http://en.wikipedia.org/wiki/Java_performance - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Scala Programming Language
http://www.scala-lang.org/ - Run Scala in Apache Tomcat in 10 minutes
http://www.softwaresecretweapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes - Fast Web Development With Scala
http://chasethedevil.blogspot.cz/2007/09/fast-web-development-with-scala.html - Top five scripting languages on the JVM
http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855 - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - CloseableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/ - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html