Obsah
1. Programovací jazyk Clojure 13: překlad programů z Clojure do bajtkódu JVM (pokračování)
2. Tvorba tříd v Clojure a jejich překlad do bajtkódu (makro gen-class)
3. Využití prefixů u názvů funkcí
4. Staticky definované datové typy v dynamicky typovaném programovacím jazyce?
6. Informace o typech předávaných parametrů i o návratovém typu metody
7. Rozhraní clojure.lang.IFn a další magie skrytá pod povrchem
1. Programovací jazyk Clojure 13: překlad programů z Clojure do bajtkódu JVM (pokračování)
V předchozí části seriálu o Javě i o virtuálním stroji Javy jsme si řekli základní informace o způsobu překladu aplikací naprogramovaných v Clojure do bajtkódu JVM. Připomeňme si, že překlad lze provést několika způsoby. V průběhu vývoje je nejčastěji prováděn překlad s generováním bajtkódu přímo ve virtuálním stroji Javy, což znamená, že se nevytváří žádné externí soubory s bajtkódem, které by bylo možné prozkoumat či přenést na počítače uživatelů. Z tohoto důvodu je pro účely distribuce aplikace a/nebo nasazení aplikace většinou vhodnější vedle zdrojových kódů dodávat i explicitně přeloženou aplikaci, a to buď ve formě souborů .class s bajtkódem, nebo alternativně ve formě Java archivu (.jar) či webového archivu (.war) – tyto archivy totiž kromě dalších souborů obsahují právě přeložený bajtkód. Existuje ještě třetí způsob překladu – vytváření tříd a k nim příslušných souborů .class přímo za běhu aplikace přes proxy třídy, což je varianta, kterou lze v samotné Javě napodobit jen velmi neohrabaně s využitím classloaderů a třídy java.lang.Compiler.
Minule jsme si taktéž ukazovali, jak se „explicitní“ překlad funkcí (prozatím skutečně pouze funkcí) provádí prakticky. V současné verzi programovacího jazyka Clojure je možné přeložit pouze kód uložený ve zdrojovém souboru, nelze tedy například přímo v interpretru přeložit jednu zvolenou funkci atd. (výjimkou je překlad využívající již zmíněnou proxy třídu; tento způsob bude popsaný příště). V nejjednodušším případě, pokud tedy prozatím nebudeme vytvářet třídy umístěné v balíčcích, je možné mít soubory se zdrojovými texty uložené v podadresáři nazvaném classes. Tyto soubory by měly mít koncovku .clj a na začátku každého souboru by měla být uvedena specifikace jmenného prostoru s názvem odpovídajícím jménu souboru se zdrojovým textem. Následující zdrojový text by tedy měl být uložený v souboru se jménem classes/AddTest1.clj:
(ns AddTest1) (defn add [x y] (+ x y))
Překlad tohoto zdrojového textu do bajtkódu JVM se provede z adresáře, v němž je umístěn výše zmíněný podadresář classes. V tomto adresáři je pro co největší jednoduchost (snadné nastavení CLASSPATH) uložen i archiv clojure-1.4.0.jar, popř. symbolický link na tento archiv. Struktura celého pracovního adresáře by mohla vypadat následovně:
| clojure-1.4.0.jar |
AddTest1.clj
\---
Překlad programu AddTest1 do bajtkódu se provede tak, že se nejdříve spustí interpret programovacího jazyka Clojure s tím, že se na CLASSPATH přidá i podadresář classes:
java -cp .;classes;clojure-1.4.0.jar clojure.main
Jakmile se interpret spustí, postačí zadat jediný příkaz spouštějící překlad:
user=> (compile 'AddTest1) AddTest1 user=>
V podadresáři classes by se měly vytvořit tři nové soubory, takže se celá struktura pracovního adresáře změní takto:
| clojure-1.4.0.jar |
AddTest1$add.class
AddTest1$loading__4784__auto__.class
AddTest1.clj
AddTest1__init.class
\---
Stručný význam jednotlivých souborů uložených v podadresáři classes:
# | Soubor | Velikost | Popis |
---|---|---|---|
1 | AddTEst1.clj | 55 | zdrojový kód příkladu v Clojure |
2 | AddTest1__init.class | 2369 | pomocné metody využívané „interpretrem“ |
3 | AddTest1$loading__4784__auto__.class | 1492 | pomocné metody využívané „interpretrem“ |
4 | AddTest1$add.class | 884 | metoda add a třída, která tuto metodu obaluje |
2. Tvorba tříd v Clojure a jejich překlad do bajtkódu (makro gen-class)
Překlad demonstračního příkladu AddTest1, s nímž jsme se seznámili v předchozí kapitole, sice skutečně vedl k vytvoření několika souborů .class a tedy i k vytvoření několika javovských tříd, ovšem ve skutečnosti v tomto případě překladač pouze vygeneroval pro každou definovanou funkci samostatnou třídu obalující příslušnou metodu. K funkci nazvané add, která byla definována ve jmenném prostoru AddTest1 tedy byla vytvořena třída nazvaná AddTest1$add, což přesně odpovídá jmenným konvencím Javy. Pro zajímavost se můžeme podívat na vnitřní strukturu této třídy. Vzhledem k tomu, že vygenerovaný bajtkód je plnohodnotným bajtkódem JVM, lze pro zjištění jeho vnitřní struktury použít libovolný (vhodný) nástroj, například nástroj javap, s nímž jsme se již v tomto seriálu několikrát setkali. Připomeňme si, že tento nástroj je standardní součástí každé instalace JDK:
% javap AddTest1$add Compiled from "AddTest1.clj" public final class AddTest1$add extends clojure.lang.AFunction{ public static final clojure.lang.Var const__0; public static {}; public AddTest1$add(); public java.lang.Object invoke(java.lang.Object, java.lang.Object); }
Naše funkce původně nazvaná add se ve skutečnosti bude volat nepřímo přes metodu Object invoke(Object, Object). Toto chování překladače Clojure sice docela dobře odpovídá filozofii funkcionálního programovacího jazyka (obecné „beztypové“ parametry), ale v praxi budeme většinou potřebovat generovat „skutečné“ javovské třídy a budeme tedy muset vyřešit minimálně dva problémy – jak sloučit několik funkcí do jediné třídy (a udělat z těchto funkcí statické či nestatické metody) a jak nahradit obecné metody typu Object invoke(Object, Object) za metody s přesněji specifikovanými datovými typy. Obě tyto problematiky lze v programovacím jazyce Clojure samozřejmě řešit, i když se popravdě řečeno nejedná o řešení elegantní: je zde patrné, že vzájemná kooperace mezi objektově orientovaným staticky typovaným jazykem a dynamicky typovaným funkcionálním jazykem je sice možná, ale nikoli zcela automatická. Nejprve si ukažme, jak se vytváří „skutečné“ třídy. Pro tyto účely se používá téměř všemocné makro nazvané gen-class.
V následujícím demonstračním příkladu je makro gen-class voláno v rámci zpracování makra ns, což je obvyklé doporučované řešení. Makro gen-class může mít velké množství volitelných parametrů, z nichž my použijeme prozatím pouze parametry dva – určení jména třídy (:name „AddTest2“) a zajištění vygenerování metody main (:main true). Ve zdrojovém kódu je navíc definována i funkce -main překládaná do metody main. Znak pomlčky na začátku názvu této metody nebyl zapsán omylem, jeho význam si vysvětlíme v navazující kapitole:
(ns AddTest2 ( :gen-class :name "AddTest2" :main true)) (defn -main [] (println "Hello world!"))
Překlad se provede stejným postupem, jako tomu bylo i u předchozího demonstračního příkladu:
% java -cp .;classes;clojure-1.4.0.jar clojure.main user=> (compile 'AddTest2) AddTest2 user=>
V adresáři classes by nyní mělo být uloženo i těchto pět souborů:
# | Soubor | Velikost |
---|---|---|
1 | AddTest2.clj | 128 |
2 | AddTest2__init.class | 2325 |
3 | AddTest2$loading__4784__auto__.class | 1494 |
4 | AddTest2$_main.class | 848 |
5 | AddTest2.class | 1744 |
Vzhledem k tomu, že ve třídě AddTest2.class je definována i metoda public static void main(String[]), můžeme tuto metodu ihned po inicializaci virtuálního stroje spustit, a to prakticky stejně, jako jakoukoli jinou aplikaci napsanou v Javě (pouze stále potřebujeme mít archiv clojure*.jar uložen na CLASSPATH):
% java -cp .;classes;clojure-1.4.0.jar AddTest2 Hello world!
3. Využití prefixů u názvů funkcí
V demonstračním příkladu z předchozí kapitoly byl před názvem funkce main uveden znak pomlčky, který byl nedílnou součástí tohoto názvu. Jedná se o takzvaný prefix používaný pro poloautomatické začleňování funkcí do vytvářených tříd, kde se z funkcí stanou plnohodnotné statické či nestatické metody. Pokud vám nevyhovuje implicitní prefix - (tj. ona pomlčka), je samozřejmě možné ho změnit a to s využitím nepovinného parametru :prefix předaného makru gen-class. V dnešním třetím demonstračním příkladu je tento parametr použit s hodnotou (řetězcem) „implementation-“, což samozřejmě znamená, že se musel změnit i název funkce main z -main (prefixem je jen pomlčka) na implementation-main (prefixem je slovo „implementation“ následované pomlčkou):
(ns AddTest3 ( :gen-class :name "AddTest3" :main true :prefix "implementation-")) (defn implementation-main [] (println "Hello world!"))
Překlad tohoto demonstračního příkladu se provede naprosto stejným způsobem, jako tomu bylo v předchozím demonstračním příkladu (v dalších kapitolách už si tuto informaci nebudeme uvádět, ale pro jednoduchost předpokládejme, že všechny zdrojové kódy budou vždy umístěny v podadresáři classes):
% java -cp .;classes;clojure-1.4.0.jar clojure.main user=> (compile 'AddTest3) AddTest3 user=>
Výsledkem překladu je čtveřice nových souborů obsahujících bajtkód a v následující tabulce jsou pro porovnání uvedena jména a velikosti souborů s bajtkódem vygenerovaným pro demonstrační příklad AddTest2 i pro příklad AddTest3. Povšimněte si, že změny postihly název bajtkódu s implementací funkce main, zatímco dalších pomocných tříd se změny prakticky nedotkly:
# | Soubor (AddTest2) | Velikost (AddTest2) | Soubor (AddTest3) | Velikost (AddTest3) |
---|---|---|---|---|
1 | AddTest2.clj | 128 | AddTest3.clj | 173 |
2 | AddTest2__init.class | 2325 | AddTest3__init.class | 2353 |
3 | AddTest2$loading__4784__auto__.class | 1494 | AddTest3$loading__4784__auto__.class | 1494 |
4 | AddTest2$_main.class | 848 | AddTest3$_main.class | 862 |
5 | AddTest2.class | 1744 | AddTest3.class | 1828 |
Zajímavější než porovnání bajtkódů vzniklých překladem obou demonstračních příkladů však bude interní struktura třídy AddTest3.class:
% javap AddTest3 public class AddTest3 extends java.lang.Object{ public static {}; public AddTest3(); public java.lang.String toString(); public boolean equals(java.lang.Object); public java.lang.Object clone(); public int hashCode(); public static void main(java.lang.String[]); }
Z předchozího výpisu je patrné, že se v této třídě skutečně nachází metoda public static void main(String[]) – tudíž je možné tuto metodu spustit po startu JVM – a navíc jsou v této třídě „poctivě“ implementovány i metody toString(), equals(), hashCode() a clone() získané překrytím metod se stejnou signaturou, které jsou deklarovány ve třídě Object, tedy ve třídě představující kořen stromu třídní hierarchie Javy. Pokud se však podíváme na obsah těchto překrytých metod, zjistíme, že se v nich ve skutečnosti volá některá z forem metody Object invoke(Object…) definované v rozhraní clojure.lang.IFn, což znamená nutnost přetypování výsledku atd. To je daň, kterou platíme za to, že se snažíme používat bajtkód navržený pro staticky typovaný jazyk. Další informace o této problematice si řekneme v navazujících kapitolách.
4. Staticky definované datové typy v dynamicky typovaném programovacím jazyce?
Dále uvedené informace není v žádném případě nutné znát pro to, abyste byli schopni vytvářet v Clojure skutečné třídy. Jedná se jen o problematiku, s níž musí bojovat a nějak ji vyřešit všichni tvůrci dynamicky typovaných programovacích jazyků určených pro běh nad virtuálním strojem Javy.
Nyní se již dostáváme k poměrně závažnému tématu – jakým způsobem je vlastně v bajtkódu zařízeno volání funkcí (či možná přesněji řečeno metod) naprogramovaných původně v Clojure? Největší problém spočívá v tom, jak se pracuje s typy parametrů a současně i s typem návratové hodnoty, protože programovací jazyk Java i jeho bajtkód je navržen s ohledem na to, že se při předávání parametrů metodám i při výpočtu aritmetických a logických výrazů používají primitivní datové typy (boolean, char, byte, short, int, long, float, double), popř. se při volání metod předávají hodnoty referenčního typu, neboli reference na objekty. Důležitější však je, že informace o typech a o počtu parametrů i informace o typu návratové hodnoty je nedílnou součástí signatury každé metody a tato signatura je uložena v bajtkódu JVM (je dokonce přímo čitelná z constant poolu).
Při překladu funkcí z Clojure do bajtkódu JVM tedy musí překladač nějakým postupem vygenerovat metody se správnou signaturou a to takovým způsobem, aby se stále jednalo o plnohodnotné metody, které je možné zavolat s libovolným typem parametrů, tj. stejným způsobem, jakým můžeme volat funkce v Clojure (počet parametrů je na druhou stranu přesně zadaný již ve chvíli, kdy je metoda vytvořena). My ve skutečnosti vlastně již víme, jak je tento úkol vyřešen, protože již v první kapitole jsme si ukázali, že následující funkce:
(defn add [x y] (+ x y))
se do bajtkódu přeloží jako metoda se signaturou public java.lang.Object invoke(java.lang.Object, java.lang.Object);, která je umístěna do „obalové“ třídy, v našem případě mající název AddTest1$add:
% javap AddTest1$add Compiled from "AddTest1.clj" public final class AddTest1$add extends clojure.lang.AFunction{ public static final clojure.lang.Var const__0; public static {}; public AddTest1$add(); public java.lang.Object invoke(java.lang.Object, java.lang.Object); }
Metoda invoke tedy jako své parametry akceptuje libovolné dva objekty, což je přesně to, co jsme vlastně zpočátku požadovali, protože v Clojure je jakákoli numerická hodnota představována instancí nějaké třídy implementující rozhraní java.lang.Number. A skutečně – pokud se podíváme na bajtkód metody invoke, zjistíme, že se jedná o velmi jednoduchou metodu, která ve skutečnosti volá metodu clojure.lang.Numbers.add(Object, Object) (až do této chvíle je tedy možné předat metodě invoke jakoukoli dvojici objektů – opět stejně jako v samotném Clojure!):
public java.lang.Object invoke(java.lang.Object, java.lang.Object); Code: 0: aload_1 ; první parametr na zásobník 1: aconst_null ; pravděpodobně jen zajištění, že se první parametr již nikde nepoužije 2: astore_1 3: aload_2 ; druhý parametr na zásobník 4: aconst_null ; pravděpodobně jen zajištění, že se první parametr již nikde nepoužije 5: astore_2 6: invokestatic #34; //Method clojure/lang/Numbers.add:(Ljava/lang/Objec t;Ljava/lang/Object;)Ljava/lang/Number; 9: areturn }
Jednoduše lze zjistit, že třída clojure.lang.Numbers skutečně obsahuje metodu add s touto signaturou:
Compiled from "Numbers.java" public class clojure.lang.Numbers extends java.lang.Object{ ... public static java.lang.Number add(java.lang.Object, java.lang.Object); ... }
Až v této metodě je v runtime proveden test na skutečný typ předávaných objektů:
public static java.lang.Number add(java.lang.Object, java.lang.Object); Code: 0: aload_0 1: invokestatic #2; //Method ops:(Ljava/lang/Object;)Lclojure/lang/Numbers$Ops; 4: aload_1 5: invokestatic #2; //Method ops:(Ljava/lang/Object;)Lclojure/lang/Numbers$Ops; 8: invokeinterface #13, 2; //InterfaceMethod clojure/lang/Numbers$Ops.combine:(Lclojure/lang/Numbers$Ops;)Lclojure/lang/Numbers$Ops; 13: aload_0 14: checkcast #3; //class java/lang/Number 17: aload_1 18: checkcast #3; //class java/lang/Number 21: invokeinterface #14, 3; //InterfaceMethod clojure/lang/Numbers$Ops.add:(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number; 26: areturn
5. Tvorba metod z funkcí
V této kapitole si ukážeme, jakým způsobem lze do generovaných tříd přidat nové metody, a to dokonce metody, u nichž je možné přesně specifikovat typ předávaných parametrů i typ návratové hodnoty. Pro přidání metod do vytvářené třídy (resp. přesněji řečeno do bajtkódu odpovídajícího této třídě) je nutné použít další parametr předávaný makru gen-class. Prozatím jsme se v předchozích kapitolách seznámili se třemi parametry tohoto makra: :name jméno_třídy, :main true/false a :prefix prefix_funkcí. Čtvrtým důležitým parametrem je parametr s názvem :methods, jemuž se předá vektor obsahující informace o všech metodách, které se mají do vytvářeného bajtkódu přidat. Informace o každé metodě je taktéž představována vektorem, jehož prvním prvkem je název vygenerované metody (bez prefixu), ve druhém prvku je uvedena specifikace typů parametrů metody (třetí vnořený vektor!) a prvkem třetím je pak typ návratové hodnoty metody. Zní to složitě, že? Ukažme si tedy, jak celý zápis :methods může vypadat:
:methods [ ; začátek vektoru obsahujícího informace o metodách [ ; začátek vektoru popisujícího metodu se signaturou "long add(long, long)" add ; jméno metody [long long] ; TYP parametrů metody (nikoli názvy těchto parametrů) long ; návratový typ metody ] ; konec vektoru popisujícího metodu se signaturou "long add(long, long)" [ ; začátek vektoru popisujícího metodu se signaturou "String add(String, String)" add ; jméno metody [String String] ; TYP parametrů metody (nikoli názvy těchto parametrů) String ; návratový typ metody ] ; konec vektoru popisujícího metodu se signaturou "String add(String, String)" ] ; konec vektoru obsahujícího informace o metodách
V praxi může použití :methods vypadat následovně – vytvoříme novou třídu nazvanou AddTest4, která bude obsahovat nestatickou metodu add akceptující dvojici parametrů typu long a jejíž návratová hodnota bude taktéž long. Tato metoda je předepsána funkcí implementation-add, kde nejsou prozatím uvedeny žádné informace o datových typech, jedná se tedy o obyčejnou funkci Clojure:
(ns AddTest4 ( :gen-class :name "AddTest4" :prefix "implementation-" :methods [ [add [long long] long] ])) (defn implementation-add [x y] (+ x y)) (defn implementation-main [] (println "Hello world!" (implementation-add 10 20)))
Jak tedy bude vypadat struktura vygenerovaného bajtkódu třídy AddTest4? S využitím nástroje javap můžeme snadno zjistit, že metoda add skutečně byla vytvořena a že její signatura přesně odpovídá našemu předpisu – public long add(long, long):
% javap AddTest4 public class AddTest4 extends java.lang.Object{ public static {}; public AddTest4(); public java.lang.String toString(); public boolean equals(java.lang.Object); public java.lang.Object clone(); public int hashCode(); public long add(long, long); public static void main(java.lang.String[]); }
Ztratili jsme však něco z obecnosti funkce implementation-main, kterou lze z Clojure volat s jakoukoli dvojicí číselných hodnot? Ve skutečnosti tomu tak není, protože ve třídě AddTest4 je sice uložena metoda public long add(long, long); (viz výpis uvedený před tímto odstavcem), ale kromě toho byl vygenerován i bajtkód třídy pojmenované AddTest4$implementation_add, v níž je stále uložen obecný tvar této funkce volané přes metodu Object invoke(Object, Object):
% javap AddTest4$implementation_add Compiled from "AddTest4.clj" public final class AddTest4$implementation_add extends clojure.lang.AFunction{ public static final clojure.lang.Var const__0; public static {}; public AddTest4$implementation_add(); public java.lang.Object invoke(java.lang.Object, java.lang.Object); }
6. Informace o typech předávaných parametrů i o návratovém typu metody
Všechny funkce, které jsme prozatím v Clojure vytvářeli, obsahovaly pouze informaci o počtu a názvu parametrů těchto funkcí (tyto údaje se zapisují do vektoru umístěného ihned za název funkce), nikoli však již údaje o typu parametrů, což dobře odpovídá způsobu zápisu funkcí a/nebo metod v dynamicky typovaných jazycích. Ve skutečnosti je však možné i v dynamicky typovaném jazyku Clojure přidat k funkcím i „statickou“ informaci o předpokládaných typech parametrů i o typu návratové hodnoty. Pro tento účel se před jméno parametru popř. za jméno funkce může přidat zápis ve tvaru ^typ. Demonstrační příklad uvedený v předchozí kapitole tedy můžeme ještě vylepšit tím, že u funkce implementation_add určíme, že oba předávané parametry by měly být typu long a i návratová hodnota by měla být typu long, což ostatně přesně koresponduje s údaji uvedenými v parametru :methods makra gen-class:
(ns AddTest5 ( :gen-class :name "AddTest5" :prefix "implementation-" :methods [[add [long long] long]])) (defn implementation-add ^long [^long x ^long y] (+ x y)) (defn implementation-main [] (println "Hello world!" (implementation-add 10 20)))
V bajtkódu vytvořené a přeložené třídy AddTest5 nenajdeme žádné „novinky“, které by nás mohly překvapit:
% javap AddTest5 public class AddTest5 extends java.lang.Object{ public static {}; public AddTest5(); public java.lang.String toString(); public boolean equals(java.lang.Object); public java.lang.Object clone(); public int hashCode(); public long add(long, long); public static void main(java.lang.String[]); }
Mnohem zajímavější je však třída AddTest5$implementation_add, protože zde oproti předchozímu demonstračnímu příkladu došlo ke dvěma významným změnám. První změnou je to, že třída nově implementuje rozhraní s poněkud neobvyklým názvem clojure.lang.IF$LLL a druhou změnou je přidání nové nestatické metody long invokePrim(long, long):
% javap AddTest5$implementation_add Compiled from "AddTest5.clj" public final class AddTest5$implementation_add extends clojure.lang.AFunction ; nově implementované rozhraní implements clojure.lang.IFn$LLL{ public static final clojure.lang.Var const__0; public static {}; public AddTest5$implementation_add(); ; nová metoda vyžadovaná rozhraním IFn$LLL public final long invokePrim(long, long); public java.lang.Object invoke(java.lang.Object, java.lang.Object); }
Podrobnosti o tom, proč vlastně byla třída AddTest5$implementation_add vytvořena přesně takovýmto způsobem, si řekneme v navazující kapitole.
7. Rozhraní clojure.lang.IFn a další magie skrytá pod povrchem
V této kapitole jsou opět uvedeny informace, které sice není zapotřebí znát pro použití jazyka Clojure v praxi, ovšem současně tyto informace osvětlují některé aspekty interní struktury Clojure i důvod, proč je konstrukce překladače pro dynamicky typované jazyky pracující nad JVM relativně složitá a někdy málo elegantní (částečné řešení spočívá ve využití instrukce invokedynamic).
Podívejme se ještě jednou na vygenerovanou třídu AddTest5. Tato třída je odvozena od třídy clojure.lang.AFunction a navíc implementuje rozhraní IFn$LLL. V samotné třídě clojure.lang.AFunction pro nás v tuto chvíli není nic zajímavého až na informaci, že se jedná o specializaci třídy clojure.lang.AFn. Až tato třída je zajímavá a to především z toho důvodu, že implementuje rozhraní clojure.lang.IFn, v němž je předepsána celá řada variant metody invoke. Ostatně se o tom sami přesvědčte:
Compiled from "IFn.java" public interface clojure.lang.IFn extends java.util.concurrent.Callable,java.lang.Runnable{ public abstract java.lang.Object invoke(); public abstract java.lang.Object invoke(java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object); public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object[]); public abstract java.lang.Object applyTo(clojure.lang.ISeq); }
Uff! Co to všechno vlastně pro nás znamená? Třída clojure.lang.AFn implementuje všechny výše vypsané metody rozhraní clojure.lang.IFn, ovšem nejedná se o finální metody, což znamená, že jakákoli třída odvozená od clojure.lang.AFn může libovolnou z těchto metod překrýt a implementovat ji po svém. Tímto poněkud „ukecaným“ způsobem je zajištěno to, že jakoukoli funkci lze v Clojure volat s libovolným množstvím parametrů (resp. s maximálně 21 parametry, pokud počítám dobře), jejichž typy se zjišťují až v runtime a současně implicitní implementace těchto metod zajišťuje správné chování funkcí při předání špatného počtu parametrů – nesmíme totiž zapomenout na to, že funkce jsou vždy překládány do bajtkódu, a to i při používání interpretru, tedy REPL.
Druhá informace o implementovaném rozhraní IFn$LLL třídou AddTest5 je taktéž důležitá. V tomto rozhraní je totiž předepsána jediná metoda se dvěma parametry typu long vracející hodnotu typu long – to jsou ostatně ony tři „L“ v názvu rozhraní:
javap IFn$LLL Compiled from "IFn.java" public interface clojure.lang.IFn$LLL{ public abstract long invokePrim(long, long); }
V Clojure existuje celá řada podobných rozhraní, která umožňují definovat signaturu metody invokePrim s různými typy parametrů a s různými typy návratové hodnot. Tyto informace lze vždy vyčíst z názvu rozhraní. Například rozhraní IFn$DLDOL předepisuje metodu invokePrim takto:
public interface clojure.lang.IFn$DLDOL{ public abstract long invokePrim(double, long, double, java.lang.Object); }
Prozatím existují tato rozhraní kombinující různé parametry a různé návratové hodnoty metody invokePrim:
clojure/lang/IFn$D clojure/lang/IFn$DD clojure/lang/IFn$DDD clojure/lang/IFn$DDDD clojure/lang/IFn$DDDDD clojure/lang/IFn$DDDDL clojure/lang/IFn$DDDDO clojure/lang/IFn$DDDL clojure/lang/IFn$DDDLD clojure/lang/IFn$DDDLL clojure/lang/IFn$DDDLO clojure/lang/IFn$DDDO clojure/lang/IFn$DDDOD clojure/lang/IFn$DDDOL clojure/lang/IFn$DDDOO clojure/lang/IFn$DDL clojure/lang/IFn$DDLD clojure/lang/IFn$DDLDD clojure/lang/IFn$DDLDL clojure/lang/IFn$DDLDO clojure/lang/IFn$DDLL clojure/lang/IFn$DDLLD clojure/lang/IFn$DDLLL clojure/lang/IFn$DDLLO clojure/lang/IFn$DDLO clojure/lang/IFn$DDLOD clojure/lang/IFn$DDLOL clojure/lang/IFn$DDLOO clojure/lang/IFn$DDO clojure/lang/IFn$DDOD clojure/lang/IFn$DDODD clojure/lang/IFn$DDODL clojure/lang/IFn$DDODO clojure/lang/IFn$DDOL clojure/lang/IFn$DDOLD clojure/lang/IFn$DDOLL clojure/lang/IFn$DDOLO clojure/lang/IFn$DDOO clojure/lang/IFn$DDOOD clojure/lang/IFn$DDOOL clojure/lang/IFn$DDOOO clojure/lang/IFn$DL clojure/lang/IFn$DLD clojure/lang/IFn$DLDD clojure/lang/IFn$DLDDD clojure/lang/IFn$DLDDL clojure/lang/IFn$DLDDO clojure/lang/IFn$DLDL clojure/lang/IFn$DLDLD clojure/lang/IFn$DLDLL clojure/lang/IFn$DLDLO clojure/lang/IFn$DLDO clojure/lang/IFn$DLDOD clojure/lang/IFn$DLDOL clojure/lang/IFn$DLDOO clojure/lang/IFn$DLL clojure/lang/IFn$DLLD clojure/lang/IFn$DLLDD clojure/lang/IFn$DLLDL clojure/lang/IFn$DLLDO clojure/lang/IFn$DLLL clojure/lang/IFn$DLLLD clojure/lang/IFn$DLLLL clojure/lang/IFn$DLLLO clojure/lang/IFn$DLLO clojure/lang/IFn$DLLOD clojure/lang/IFn$DLLOL clojure/lang/IFn$DLLOO clojure/lang/IFn$DLO clojure/lang/IFn$DLOD clojure/lang/IFn$DLODD clojure/lang/IFn$DLODL clojure/lang/IFn$DLODO clojure/lang/IFn$DLOL clojure/lang/IFn$DLOLD clojure/lang/IFn$DLOLL clojure/lang/IFn$DLOLO clojure/lang/IFn$DLOO clojure/lang/IFn$DLOOD clojure/lang/IFn$DLOOL clojure/lang/IFn$DLOOO clojure/lang/IFn$DO clojure/lang/IFn$DOD clojure/lang/IFn$DODD clojure/lang/IFn$DODDD clojure/lang/IFn$DODDL clojure/lang/IFn$DODDO clojure/lang/IFn$DODL clojure/lang/IFn$DODLD clojure/lang/IFn$DODLL clojure/lang/IFn$DODLO clojure/lang/IFn$DODO clojure/lang/IFn$DODOD clojure/lang/IFn$DODOL clojure/lang/IFn$DODOO clojure/lang/IFn$DOL clojure/lang/IFn$DOLD clojure/lang/IFn$DOLDD clojure/lang/IFn$DOLDL clojure/lang/IFn$DOLDO clojure/lang/IFn$DOLL clojure/lang/IFn$DOLLD clojure/lang/IFn$DOLLL clojure/lang/IFn$DOLLO clojure/lang/IFn$DOLO clojure/lang/IFn$DOLOD clojure/lang/IFn$DOLOL clojure/lang/IFn$DOLOO clojure/lang/IFn$DOO clojure/lang/IFn$DOOD clojure/lang/IFn$DOODD clojure/lang/IFn$DOODL clojure/lang/IFn$DOODO clojure/lang/IFn$DOOL clojure/lang/IFn$DOOLD clojure/lang/IFn$DOOLL clojure/lang/IFn$DOOLO clojure/lang/IFn$DOOO clojure/lang/IFn$DOOOD clojure/lang/IFn$DOOOL clojure/lang/IFn$DOOOO clojure/lang/IFn$L clojure/lang/IFn$LD clojure/lang/IFn$LDD clojure/lang/IFn$LDDD clojure/lang/IFn$LDDDD clojure/lang/IFn$LDDDL clojure/lang/IFn$LDDDO clojure/lang/IFn$LDDL clojure/lang/IFn$LDDLD clojure/lang/IFn$LDDLL clojure/lang/IFn$LDDLO clojure/lang/IFn$LDDO clojure/lang/IFn$LDDOD clojure/lang/IFn$LDDOL clojure/lang/IFn$LDDOO clojure/lang/IFn$LDL clojure/lang/IFn$LDLD clojure/lang/IFn$LDLDD clojure/lang/IFn$LDLDL clojure/lang/IFn$LDLDO clojure/lang/IFn$LDLL clojure/lang/IFn$LDLLD clojure/lang/IFn$LDLLL clojure/lang/IFn$LDLLO clojure/lang/IFn$LDLO clojure/lang/IFn$LDLOD clojure/lang/IFn$LDLOL clojure/lang/IFn$LDLOO clojure/lang/IFn$LDO clojure/lang/IFn$LDOD clojure/lang/IFn$LDODD clojure/lang/IFn$LDODL clojure/lang/IFn$LDODO clojure/lang/IFn$LDOL clojure/lang/IFn$LDOLD clojure/lang/IFn$LDOLL clojure/lang/IFn$LDOLO clojure/lang/IFn$LDOO clojure/lang/IFn$LDOOD clojure/lang/IFn$LDOOL clojure/lang/IFn$LDOOO clojure/lang/IFn$LL clojure/lang/IFn$LLD clojure/lang/IFn$LLDD clojure/lang/IFn$LLDDD clojure/lang/IFn$LLDDL clojure/lang/IFn$LLDDO clojure/lang/IFn$LLDL clojure/lang/IFn$LLDLD clojure/lang/IFn$LLDLL clojure/lang/IFn$LLDLO clojure/lang/IFn$LLDO clojure/lang/IFn$LLDOD clojure/lang/IFn$LLDOL clojure/lang/IFn$LLDOO clojure/lang/IFn$LLL clojure/lang/IFn$LLLD clojure/lang/IFn$LLLDD clojure/lang/IFn$LLLDL clojure/lang/IFn$LLLDO clojure/lang/IFn$LLLL clojure/lang/IFn$LLLLD clojure/lang/IFn$LLLLL clojure/lang/IFn$LLLLO clojure/lang/IFn$LLLO clojure/lang/IFn$LLLOD clojure/lang/IFn$LLLOL clojure/lang/IFn$LLLOO clojure/lang/IFn$LLO clojure/lang/IFn$LLOD clojure/lang/IFn$LLODD clojure/lang/IFn$LLODL clojure/lang/IFn$LLODO clojure/lang/IFn$LLOL clojure/lang/IFn$LLOLD clojure/lang/IFn$LLOLL clojure/lang/IFn$LLOLO clojure/lang/IFn$LLOO clojure/lang/IFn$LLOOD clojure/lang/IFn$LLOOL clojure/lang/IFn$LLOOO clojure/lang/IFn$LO clojure/lang/IFn$LOD clojure/lang/IFn$LODD clojure/lang/IFn$LODDD clojure/lang/IFn$LODDL clojure/lang/IFn$LODDO clojure/lang/IFn$LODL clojure/lang/IFn$LODLD clojure/lang/IFn$LODLL clojure/lang/IFn$LODLO clojure/lang/IFn$LODO clojure/lang/IFn$LODOD clojure/lang/IFn$LODOL clojure/lang/IFn$LODOO clojure/lang/IFn$LOL clojure/lang/IFn$LOLD clojure/lang/IFn$LOLDD clojure/lang/IFn$LOLDL clojure/lang/IFn$LOLDO clojure/lang/IFn$LOLL clojure/lang/IFn$LOLLD clojure/lang/IFn$LOLLL clojure/lang/IFn$LOLLO clojure/lang/IFn$LOLO clojure/lang/IFn$LOLOD clojure/lang/IFn$LOLOL clojure/lang/IFn$LOLOO clojure/lang/IFn$LOO clojure/lang/IFn$LOOD clojure/lang/IFn$LOODD clojure/lang/IFn$LOODL clojure/lang/IFn$LOODO clojure/lang/IFn$LOOL clojure/lang/IFn$LOOLD clojure/lang/IFn$LOOLL clojure/lang/IFn$LOOLO clojure/lang/IFn$LOOO clojure/lang/IFn$LOOOD clojure/lang/IFn$LOOOL clojure/lang/IFn$LOOOO clojure/lang/IFn$OD clojure/lang/IFn$ODD clojure/lang/IFn$ODDD clojure/lang/IFn$ODDDD clojure/lang/IFn$ODDDL clojure/lang/IFn$ODDDO clojure/lang/IFn$ODDL clojure/lang/IFn$ODDLD clojure/lang/IFn$ODDLL clojure/lang/IFn$ODDLO clojure/lang/IFn$ODDO clojure/lang/IFn$ODDOD clojure/lang/IFn$ODDOL clojure/lang/IFn$ODDOO clojure/lang/IFn$ODL clojure/lang/IFn$ODLD clojure/lang/IFn$ODLDD clojure/lang/IFn$ODLDL clojure/lang/IFn$ODLDO clojure/lang/IFn$ODLL clojure/lang/IFn$ODLLD clojure/lang/IFn$ODLLL clojure/lang/IFn$ODLLO clojure/lang/IFn$ODLO clojure/lang/IFn$ODLOD clojure/lang/IFn$ODLOL clojure/lang/IFn$ODLOO clojure/lang/IFn$ODO clojure/lang/IFn$ODOD clojure/lang/IFn$ODODD clojure/lang/IFn$ODODL clojure/lang/IFn$ODODO clojure/lang/IFn$ODOL clojure/lang/IFn$ODOLD clojure/lang/IFn$ODOLL clojure/lang/IFn$ODOLO clojure/lang/IFn$ODOO clojure/lang/IFn$ODOOD clojure/lang/IFn$ODOOL clojure/lang/IFn$ODOOO clojure/lang/IFn$OL clojure/lang/IFn$OLD clojure/lang/IFn$OLDD clojure/lang/IFn$OLDDD clojure/lang/IFn$OLDDL clojure/lang/IFn$OLDDO clojure/lang/IFn$OLDL clojure/lang/IFn$OLDLD clojure/lang/IFn$OLDLL clojure/lang/IFn$OLDLO clojure/lang/IFn$OLDO clojure/lang/IFn$OLDOD clojure/lang/IFn$OLDOL clojure/lang/IFn$OLDOO clojure/lang/IFn$OLL clojure/lang/IFn$OLLD clojure/lang/IFn$OLLDD clojure/lang/IFn$OLLDL clojure/lang/IFn$OLLDO clojure/lang/IFn$OLLL clojure/lang/IFn$OLLLD clojure/lang/IFn$OLLLL clojure/lang/IFn$OLLLO clojure/lang/IFn$OLLO clojure/lang/IFn$OLLOD clojure/lang/IFn$OLLOL clojure/lang/IFn$OLLOO clojure/lang/IFn$OLO clojure/lang/IFn$OLOD clojure/lang/IFn$OLODD clojure/lang/IFn$OLODL clojure/lang/IFn$OLODO clojure/lang/IFn$OLOL clojure/lang/IFn$OLOLD clojure/lang/IFn$OLOLL clojure/lang/IFn$OLOLO clojure/lang/IFn$OLOO clojure/lang/IFn$OLOOD clojure/lang/IFn$OLOOL clojure/lang/IFn$OLOOO clojure/lang/IFn$OOD clojure/lang/IFn$OODD clojure/lang/IFn$OODDD clojure/lang/IFn$OODDL clojure/lang/IFn$OODDO clojure/lang/IFn$OODL clojure/lang/IFn$OODLD clojure/lang/IFn$OODLL clojure/lang/IFn$OODLO clojure/lang/IFn$OODO clojure/lang/IFn$OODOD clojure/lang/IFn$OODOL clojure/lang/IFn$OODOO clojure/lang/IFn$OOL clojure/lang/IFn$OOLD clojure/lang/IFn$OOLDD clojure/lang/IFn$OOLDL clojure/lang/IFn$OOLDO clojure/lang/IFn$OOLL clojure/lang/IFn$OOLLD clojure/lang/IFn$OOLLL clojure/lang/IFn$OOLLO clojure/lang/IFn$OOLO clojure/lang/IFn$OOLOD clojure/lang/IFn$OOLOL clojure/lang/IFn$OOLOO clojure/lang/IFn$OOOD clojure/lang/IFn$OOODD clojure/lang/IFn$OOODL clojure/lang/IFn$OOODO clojure/lang/IFn$OOOL clojure/lang/IFn$OOOLD clojure/lang/IFn$OOOLL clojure/lang/IFn$OOOLO clojure/lang/IFn$OOOOD clojure/lang/IFn$OOOOL
V předchozí části tohoto seriálu jsme si řekli, že Clojure využívá pouze malé množství primitivních typů jazyka Java a toto je jeden z důvodů, proč jich není podporováno více. Pokud by se například kromě typu double podporoval ještě typ float, musel by počet rozhraní IFn$*** být ještě mnohem větší!
8. Statické metody
Ve výčtu metod zapisovaného do parametru :methods makra gen-class lze určit, které metody jsou statické a které nikoli. Připomeňme si, že nestatické metody se od metod statických liší především tím, že se jim při volání automaticky předává i reference na objekt, pro nějž je nestatická metoda volána. Naproti tomu statické metody nemají přístup k atributům tohoto objektu (mají však samozřejmě přístup k atributům třídy). Pro vytvoření statické metody postačuje před vektor obsahující informace o metodě přidat metainformaci ^{:static true}. To je vše – pouze na základě této informace se vytvoří metoda, která má v bajtkódu nastaven příznak ACC_STATIC a bude se jí předávat o jeden parametr méně – bude tedy chybět neviditelný parametr this:
(ns AddTest6 ( :gen-class :name "AddTest6" :prefix "implementation-" :methods [^{:static true} [add [long long] long]])) (defn implementation-add [^long x ^long y] (^long + x y)) (defn implementation-main [] (println "Hello world!" (implementation-add 10 20)))
O tom, že je skutečně vygenerována statická metoda, se můžeme jednoduše přesvědčit prozkoumáním interní struktury vytvořeného souboru AddTest6.class s bajkódem:
% javap AddTest6 public class AddTest6 extends java.lang.Object{ public static {}; public AddTest6(); public java.lang.String toString(); public boolean equals(java.lang.Object); public java.lang.Object clone(); public int hashCode(); public static long add(long, long); public static void main(java.lang.String[]); }
Na závěr si musíme ještě říci, že přidání typů k funkcím (jedná se o metainformaci) v Clojure ve skutečnosti neznamená, že by bylo možné jen takto jednoduše vytvořit přetížené metody, tj. metody se stejným názvem, které se od sebe liší typem a/nebo počtem parametrů. Podívejme se na následující příklad, v němž je skrytá určitá zrada, kterou však překladač dopředu neohlásí, a to z toho prostého důvodu, že tuto zradu vlastně nemůže u dynamicky typovaného jazyka vždy na 100% předvídat:
(ns AddTest7 ( :gen-class :name "AddTest7" :prefix "implementation-" :methods [ ^{:static true} [add [long long] long] ^{:static true} [add [double double] double] ^{:static true} [add [String String] String] ])) (defn implementation-add [^long x ^long y] (^long + x y)) (defn implementation-add [^double x ^double y] (^double + x y)) (defn implementation-add [^String x ^String y] (^String str x y)) (defn implementation-main [] (println "Hello world!"))
Mohlo by se zdát, že je vše v pořádku a že jsme takto jednoduše nadefinovali trojici funkcí implementation-add, které se přeloží do odpovídajících metod add. I průzkum bajtkódu vytvořené třídy neodhalí žádný problém, spíše naopak:
% javap AddTest7 public class AddTest7 extends java.lang.Object{ public static {}; public AddTest7(); public java.lang.String toString(); public boolean equals(java.lang.Object); public java.lang.Object clone(); public int hashCode(); public static long add(long, long); public static double add(double, double); public static java.lang.String add(java.lang.String, java.lang.String); public static void main(java.lang.String[]); }
Ve skutečnosti však došlo k tomu, že poslední funkce implementation-add přemazala definici předchozích funkcí, a to úplně stejným způsobem, jako by se to stalo v interpretru jazyka Clojure. To znamená, že i když je ve vygenerovaném bajtkódu třídy AddTest7.class uvedena trojice metod add s různými parametry, bude se z těchto metod ve skutečnosti vždy volat přeložená funkce implementation-add, v jejímž těle se budou spojovat dva řetězce pomocí str. Pokud se této funkci předají parametry jiného typu než dvojice řetězců (AddTest7.add(1,2)), dojde k běhové chybě!
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