Obsah
1. Programovací jazyk Julia: metaprogramování, makra a AST
2. Parsing zdrojového kódu, použití funkce parse()
3. Interní reprezentace výrazů
5. Složitější výrazy a rekurzivní podoba AST
6. Zobrazení S-výrazu parsované části zdrojového kódu
7. Vyhodnocení výrazů s využitím funkce eval()
8. Programová či „ruční“ konstrukce výrazů
9. Použití znaku : namísto konstruktoru Expr
10. Tvorba výrazů a použití interpolace
11. Makrosystém programovacího jazyka Julia
1. Programovací jazyk Julia: metaprogramování, makra a AST
Programovací jazyk Julia byl navržen takovým způsobem, aby byl snadno pochopitelný, čitelný a taktéž s ohledem na jeho případnou rozšiřitelnost v budoucnosti. Ovšem fakt, že tvorba krátkých skriptů i rozsáhlejších programů v jazyku Julia je poměrně jednoduchá, nemusí nutně znamenat, že v Julii není možné v případě potřeby vytvářet i složitější konstrukce popř. si ušetřit práci a nechat si část programového kódu (nikoli nutně zdrojového kódu) vygenerovat. Je to umožněno především díky existenci makrosystému, který je založen na manipulaci se symboly, které vzniknou po parsingu zdrojového kódu a které tvoří AST (Abstract Syntax Tree). Ovšem na tomto místě je vhodné poznamenat, že makra používaná v jazyku Julia nemají prakticky nic společného s makry, které známe například z jazyků C či C++, popř. s makry, na jejichž zpracování je založen jazyk m4.
V jazycích C, C++ i m4 se totiž makra používají pro „pouhé“ provádění textových substitucí, zatímco makrosystém implementovaný v jazyku Julia je založen na modifikaci AST, což umožňuje mnohem hlubší zásahy do kódu (například přidání nových operátorů). V tomto ohledu má jazyk Julia velmi blízko k LISPovským jazykům, v nichž je většinou makrosystém prakticky nedílnou součástí programovacího jazyka, protože jsou v něm realizovány mnohdy i základní programové konstrukce (navíc je LISP homoikonickým jazykem, což situaci dále zjednodušuje). Asi nejtypičtějším příkladem použití maker v LISPu je makro loop použité v Common Lispu (na druhou stranu někteří vývojáři soudí, že podobná makra zbytečně do Common Lispu přidávají imperativní kód). Některé vlastnosti tohoto makra jsou popsány na stránce http://www.ai.sri.com/pkarp/loop.html.
Poznámka: v odkazech na další zdroje naleznete i několik článků o makrech v jazyku Clojure. Není to náhoda, protože způsob tvorby maker i použitou terminologii nalezneme právě v jazyku Julia.
2. Parsing zdrojového kódu, použití funkce parse()
Ještě předtím, než si popíšeme způsob tvorby maker v jazyku Julia, je vhodné se zmínit o tom, jakým způsobem je vlastně zpracováván zdrojový kód zapisovaný uživatelem do interaktivní smyčky REPL či kód delších programů a knihoven načítaný z externích souborů. Jednotlivé výrazy a z nich tvořené ucelené bloky kódu (například programové smyčky) jsou předány do funkce nazvané parse(). Tato funkce analyzuje text (s použitím lexikální a syntaktické analýzy), zkonstruuje z něho AST a nakonec vrátí objekt typu Expr, který vlastně (poněkud zjednodušeně řečeno) reprezentuje AST daného výrazu či bloku kódu. Funkce parse() ve své základní podobě akceptuje řetězec, v němž nalezne a analyzuje první ucelený výraz, ovšem lze ji volat i s dalšími parametry, které ovlivní její činnost. To je ostatně patrné i při pohledu do dokumentace:
help?> parse search: parse parseip parseint parsefloat ParseError sparse sparsevec parse(str, start; greedy=true, raise=true) Parse the expression string and return an expression (which could later be passed to eval for execution). start is the index of the first character to start parsing. If greedy is true (default), parse will try to consume as much input as it can; otherwise, it will stop as soon as it has parsed a valid expression. Incomplete but otherwise syntactically valid expressions will return Expr(:incomplete, "(error message)"). If raise is true (default), syntax errors other than incomplete expressions will raise an error. If raise is false, parse will return an expression that will raise an error upon evaluation. parse(str; raise=true) Parse the expression string greedily, returning a single expression. An error is thrown if there are additional characters after the first expression. If raise is true (default), syntax errors will raise an error; otherwise, parse will return an expression that will raise an error upon evaluation. parse(type, str, [base]) Parse a string as a number. If the type is an integer type, then a base can be specified (the default is 10). If the type is a floating point type, the string is parsed as a decimal floating point number. If the string does not contain a valid number, an error is raised.
Podívejme se, jak vypadá výsledek volání funkce parse(). Zdánlivě a minimálně na první pohled se nejedná o žádnou „velkou vědu“, neboť výsledkem volání je opticky prakticky stejný text, který je akorát umístěn do závorek, před nimiž je znak : (dvojtečky). Jak však uvidíme dále, jedná se pouze o zkrácený zápis obsahu objektu typu Expr:
julia& parse("1+2") :(1 + 2)
julia& parse("a+b") :(a + b)
julia& parse("a+b*2") :(a + b * 2)
julia& parse("1+2*3") :(1 + 2 * 3)
julia& parse("1<2 && 3<=4") :(1 < 2 && 3 <= 4)
julia& parse("while i&0; println(i); i=i-1; end") :(while i & 0 # none, line 1: println(i) # none, line 1: i = i - 1 end)
3. Interní reprezentace výrazů
Každý objekt typu Expr obsahuje tři atributy pojmenované head, args a typ. Atribut head obsahuje symbol popisující typ výrazu, atribut args jeho argumenty (mohou se zde nacházet symboly, rekurzivně vnořené výrazy popř. konstanty a literály) a konečně atribut typ obsahuje typ výsledku. V případě jednoduchých výrazů obsahuje atribut args pouze pole symbolů a konstant. Podívejme se nejprve, jak k atributům parsovaného výrazu přistupovat. Nejprve si uložíme objekt typu Expr do proměnné:
julia& expression1=parse("1+2") :(1 + 2)
Posléze si již můžeme jednoduše vypsat typ výrazu:
julia& expression1.head :call
Vidíme, že typ výrazu je symbol call značící, že se jedná o volání nějaké funkce (již víme, že operátory se dají volat i jako běžné funkce).
Dále si můžeme vypsat návratový typ. Zde se neděje nic překvapivého:
julia& expression1.typ Any
Podle očekávání je nejzajímavějším atributem atribut args obsahující argumenty volání. Předávají se tři parametry – symbol představující volanou funkci (+) a její dva parametry:
julia& expression1.args 3-element Array{Any,1}: :+ 1 2
Podobně si lze zobrazit i argumenty nepatrně složitější funkce se třemi parametry:
julia& expression2=parse("1+2+3") :(1 + 2 + 3) julia& expression2.args 4-element Array{Any,1}: :+ 1 2 3
Nebo volání „obyčejné“ funkce:
julia& expression3=parse("sin(3.1415)") :(sin(3.1415)) julia& expression3.args 2-element Array{Any,1}: :sin 3.1415
julia& expression4=parse("length(\"asd\")") :(length("asd")) julia& expression4.args 2-element Array{Any,1}: :length "asd"
4. Použití funkce dump()
Přímý přístup k atributům objektu typu Expr je mnohdy zbytečně zdlouhavý. Pokud je nutné častěji zkoumat AST vygenerovaný výše popsanou funkcí parse(), může být výhodnější použít další užitečnou funkci nazvanou jednoduše dump(). Této funkci lze předat prakticky jakýkoli objekt, samozřejmě včetně objektů typu Expr. Výsledkem je textová podoba interní struktury objektu (v případě objektu Expr tedy atributy head, args i typ):
help?& dump search: dump xdump randjump reducedim mapreducedim module_name dump(x) Show all user-visible structure of a value.
Výše zmíněné výrazy si tedy můžeme přímo v REPLu analyzovat i následujícím způsobem:
julia& expression1=parse("1+2") :(1 + 2) julia& dump(expression1) Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Int64 1 3: Int64 2 typ: Any
julia& expression2=parse("1+2+3") :(1 + 2 + 3) julia& dump(expression2) Expr head: Symbol call args: Array(Any,(4,)) 1: Symbol + 2: Int64 1 3: Int64 2 4: Int64 3 typ: Any
julia& expression3=parse("sin(3.1415)") :(sin(3.1415)) julia& dump(expression3) Expr head: Symbol call args: Array(Any,(2,)) 1: Symbol sin 2: Float64 3.1415 typ: Any
julia& expression4=parse("length(\"asd\")") :(length("asd")) julia& dump(expression4) Expr head: Symbol call args: Array(Any,(2,)) 1: Symbol length 2: ASCIIString "asd" typ: Any
5. Složitější výrazy a rekurzivní podoba AST
Při pohledu na výsledky vypisované funkcí dump() si možná čtenář pokládá otázku, proč se vlastně bavíme o AST (Abstract Syntax Tree), když je výsledkem parsingu jednoduchá datová struktura popisující volání nějaké funkce s parametry. Prozatím jsme si totiž ukazovali velmi jednoduché příklady výrazů, které skutečně vedly k takto jednoduchým výsledkům. Ovšem programovací jazyk Julia podle očekávání musí umět zpracovat i složitější výrazy a programové konstrukce. Podívejme se například, co se stane, když namísto výrazu „1+2+3“ použijeme výraz „1+(2+3)“:
julia& expression2=parse("1+2+3") :(1 + 2 + 3) julia& dump(expression2) Expr head: Symbol call args: Array(Any,(4,)) 1: Symbol + 2: Int64 1 3: Int64 2 4: Int64 3 typ: Any
julia& expression5=parse("1+(2+3)") :(1 + (2 + 3)) julia& dump(expression5) Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Int64 1 3: Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Int64 2 3: Int64 3 typ: Any typ: Any
Vidíme, že se zde volá funkce specifikovaná symbolem +, prvním parametrem této funkce je konstanta 1 a druhý parametr je rekurzivně vyjádřen dalším objektem typu Expr. Zde již tedy začíná být patrné, že se skutečně jedná o rekurzivní datovou strukturu.
I následující výraz bude převeden na strom, přesněji řečeno na dva objekty Expr zanořené do kořenového Expr:
julia& expression6=parse("1<2 && 4>=3") :(1 < 2 && 4 >= 3) julia& dump(expression6) Expr head: Symbol && args: Array(Any,(2,)) 1: Expr head: Symbol comparison args: Array(Any,(3,)) 1: Int64 1 2: Symbol < 3: Int64 2 typ: Any 2: Expr head: Symbol comparison args: Array(Any,(3,)) 1: Int64 4 2: Symbol >= 3: Int64 3 typ: Any typ: Any
Podobně vypadá AST pro konstrukci podmínky:
julia& expression7=parse("if 2>1 then; println(\"vetsi\"); end") :(if 2 > 1 # none, line 1: then # none, line 1: println("vetsi") end) julia& dump(expression7) Expr head: Symbol if args: Array(Any,(2,)) 1: Expr head: Symbol comparison args: Array(Any,(3,)) 1: Int64 2 2: Symbol > 3: Int64 1 typ: Any 2: Expr head: Symbol block args: Array(Any,(4,)) 1: LineNumberNode file: Symbol none line: Int64 1 2: Symbol then 3: LineNumberNode file: Symbol none line: Int64 1 4: Expr head: Symbol call args: Array(Any,(2,)) typ: Any typ: Any typ: Any
Posledním příkladem, který si ukážeme, je parsing nekonečné smyčky:
julia& expression8=parse("while true; println(\"looping\"); end") :(while true # none, line 1: println("looping") end) julia& dump(expression8) Expr head: Symbol while args: Array(Any,(2,)) 1: Bool true 2: Expr head: Symbol block args: Array(Any,(2,)) 1: LineNumberNode file: Symbol none line: Int64 1 2: Expr head: Symbol call args: Array(Any,(2,)) typ: Any typ: Any typ: Any
6. Zobrazení S-výrazu parsované části zdrojového kódu
Programátoři, kteří se již seznámili s některých jazykem patřícím do LISPovské rodiny, tedy například s AutoLISPem, Common Lispem, Scheme či Clojure, ví, že se v těchto jazycích program zapisuje s použitím takzvaných S-výrazů (symolických výrazů) neboli S-expression (někdy se tento název „vtipně“ zkracuje na sexprs nebo sexps). S-výrazy vlastně přímo vyjadřují podobu AST, což mj. naznačuje, proč je tvorba maker v LISPovských jazycích celkem jednoduchá a přímočará. Vzhledem k tomu, že díky použití S-výrazů lze AST reprezentovat v poměrně čitelné a současně i kompaktní podobě (jako seznam vnořených seznamů, symbolů a konstant), obsahuje i knihovna programovacího jazyka Julia možnost, jak si nechat vypsat libovolný objekt typu Expr „lispovským způsobem“. Vše potřebné zařídí funkce Meta.show_sexpr(), což je ostatně patrné i při pohledu na další příklady (pro ilustraci používám stejné příklady, jako v předchozích kapitolách):
julia& expression1=parse("1+2") :(1 + 2) julia& Meta.show_sexpr(expression1) (:call, :+, 1, 2)
julia& expression2=parse("1+2+3") :(1 + 2 + 3) julia& Meta.show_sexpr(expression2) (:call, :+, 1, 2, 3)
julia& expression3=parse("sin(3.1415)") :(sin(3.1415)) julia& Meta.show_sexpr(expression3) (:call, :sin, 3.1415)
julia& expression4=parse("length(\"asd\")") :(length("asd")) julia& Meta.show_sexpr(expression4) (:call, :length, "asd")
julia& expression5=parse("1+(2+3)") :(1 + (2 + 3)) julia& Meta.show_sexpr(expression5) (:call, :+, 1, (:call, :+, 2, 3))
julia& expression6=parse("1<2 && 4>=3") :(1 < 2 && 4 >= 3) julia& Meta.show_sexpr(expression6) (:&&, (:comparison, 1, :<, 2), (:comparison, 4, :(>=), 3))
julia& expression7=parse("if 2>1 then; println(\"vetsi\"); end") :(if 2 > 1 # none, line 1: then # none, line 1: println("vetsi") end) julia& Meta.show_sexpr(expression7) (:if, (:comparison, 2, :>, 1), (:block, :( # none, line 1:), :then, :( # none, line 1:), (:call, :println, "vetsi") ))
julia& expression8=parse("while true; println(\"looping\"); end") :(while true # none, line 1: println("looping") end) julia& Meta.show_sexpr(expression8) (:while, true, (:block, :( # none, line 1:), (:call, :println, "looping") ))
julia& Meta.show_sexpr(parse("1+2*(3+4)")) (:call, :+, 1, (:call, :*, 2, (:call, :+, 3, 4)))
7. Vyhodnocení výrazů s využitím funkce eval()
S první částí činnosti interpretru programovacího jazyka Julia jsme se již seznámili v předchozím textu – je jím parser. Druhá část je nejméně důležitá a z uživatelského hlediska je představována funkcí eval(), které se předá AST a výsledkem volání funkce eval() je příslušný vyhodnocený výraz a popř. i nějaké vedlejší efekty (například výpis textu funkcí println, vytvoření nové proměnné, vytvoření a spuštění nového vlákna atd.). Základní použití funkce eval() je velmi jednoduché – budeme jí totiž zpočátku předávat pouze AST korektně vygenerované funkcí parse():
julia& expression1=parse("1+2") :(1 + 2) julia& eval(expression1) 3
julia& expression2=parse("1+2+3") :(1 + 2 + 3) julia& eval(expression2) 6
julia& expression3=parse("sin(3.1415)") :(sin(3.1415)) julia& eval(expression3) 9.265358966049026e-5
julia& expression4=parse("length(\"asd\")") :(length("asd")) julia& eval(expression4) 3
julia& expression5=parse("1+(2+3)") :(1 + (2 + 3)) julia& eval(expression5) 6
julia& expression6=parse("1<2 && 4>=3") :(1 < 2 && 4 >= 3) julia& eval(expression6) true
julia& expression7=parse("if 2>1 then; println(\"vetsi\"); end") :(if 2 > 1 # none, line 1: then # none, line 1: println("vetsi") end) julia& eval(expression7) vetsi
Poznámka: v tomto případě není řetězec „vetsi“ výsledkem funkce eval(), ale pouhým vedlejším efektem volání funkce println().
julia& expression8=parse("while true; println(\"looping\"); end") :(while true # none, line 1: println("looping") end) julia& eval(expression7) looping looping looping looping looping looping looping ...
V tomto případě funkce eval() podle předpokladů nikdy neskončí.
Povšimněte si toho, že výraz může obsahovat neznámé proměnné, což při jeho parsingu nevadí. Ovšem při vyhodnocování již taková proměnná musí existovat:
julia& expression9=parse("i/(i+1.0)") :(i / (i + 1.0)) julia& dump(expression9) Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol / 2: Symbol i 3: Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Symbol i 3: Float64 1.0 typ: Any typ: Any julia& eval(expression9) ERROR: UndefVarError: i not defined julia& i=3 3 julia& eval(expression9) 0.75
8. Programová či „ruční“ konstrukce výrazů
Nyní již máme k dispozici dostatek informací k tomu, abychom mohli začít konstruovat vlastní výrazy. Nejsložitější (resp. přesněji řečeno „nejukecanější“) je explicitní použití konstruktoru objektů typu Expr. Zkusme si nyní vytvořit výraz odpovídající zápisu „1+2*3“:
julia& my_expression=Expr(:call, :+, 1, Expr(:call, :*, 2, 3)) :(1 + 2 * 3) julia& dump(my_expression) Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Int64 1 3: Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol * 2: Int64 2 3: Int64 3 typ: Any typ: Any julia& eval(my_expression) 7
9. Použití znaku : namísto konstruktoru Expr
V demonstračním příkladu ukázaném v předchozí kapitole se nám sice výraz zkonstruovat podařilo, ale samotný zápis jeho konstruktoru jistě neoplývá čitelností ani srozumitelností – takto by se nám makra tvořila velmi špatně. Základem pro radikální zjednodušení konstrukce výrazů je použití symbolu : (dvojtečka), který se používá pro takzvanou „citaci“ (quoting):
julia& my_expression=:(1+2*3) :(1 + 2 * 3) julia& dump(my_expression) Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Int64 1 3: Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol * 2: Int64 2 3: Int64 3 typ: Any typ: Any julia& eval(my_expression) 7
Pro delší výrazy, například pro zápis programových smyček, je však i použití dvojtečky zbytečně nečitelné. Zde přichází na řadu syntaktický cukr představovaný slovy quote a end:
julia& my_expression=quote 1+2*3 end quote # none, line 2: 1 + 2 * 3 end julia& dump(my_expression) Expr head: Symbol block args: Array(Any,(2,)) 1: LineNumberNode file: Symbol none line: Int64 2 2: Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Int64 1 3: Expr head: Symbol call args: Array(Any,(3,)) typ: Any typ: Any typ: Any julia& eval(my_expression) 7
Použití při deklaraci programové smyčky:
julia& my_loop=quote while i<10 println(i) i=i+1 end end quote # none, line 2: while i < 10 # none, line 3: println(i) # none, line 4: i = i + 1 end end
Poznámka: první klíčové slovo end uzavírá programovou smyčku, druhé slovo end pak blok quote.
Použití takto vytvořeného objektu typu Expr (především reprezentace AST) je přímočaré:
julia& i=0 0 julia& eval(my_loop) 0 1 2 3 4 5 6 7 8 9
10. Tvorba výrazů a použití interpolace
Kromě operace citace (quote) se při tvorbě maker ještě velmi často uplatňuje opačná operace – takzvaná interpolace. Interpolace se zapisuje s využitím znaku $ (dolar) a ruší efekt citace pro následující symbol (tento symbol je ihned vyhodnocen – do AST se zapíše až jeho vyhodnocená forma). Ukažme si jednoduchý příklad a rozdíl mezi přímým vyhodnocením výrazu, parsingem výrazu s použitím citace a interpolace a jeho následným vyhodnocením.
Přímé vyhodnocení výrazu:
julia& x=42 42 julia& 1+2*x 85
Vytvoření výrazu citací, ale bez interpolace:
julia& ex2=:(1+2*x) :(1 + 2x) julia& dump(ex2) Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Int64 1 3: Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol * 2: Int64 2 3: Symbol x zde se stále pracuje se jménem proměnné typ: Any typ: Any julia& eval(ex2) 85
Vytvoření výrazu citací s interpolací. Proměnná x se vyhodnotí a do AST je uložen výsledek:
julia& ex3=:(1+2*$x) :(1 + 2 * 42) julia& dump(ex3) Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Int64 1 3: Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol * 2: Int64 2 3: Int64 42 zde se pracuje s vyhodnoceným výrazem/symbolem x typ: Any typ: Any julia& eval(ex3) 85
Interpolaci lze použít i na složitější výraz, nejenom na jediný symbol. V dalším příkladu se již při konstrukci AST vyhodnotí 2*42=84 a teprve hodnota 84 je skutečně uložena do AST:
julia& ex4=:(1+$(2*x)) :(1 + 84) julia& dump(ex4) Expr head: Symbol call args: Array(Any,(3,)) 1: Symbol + 2: Int64 1 3: Int64 84 typ: Any julia& eval(ex4) 85
Na citaci se můžeme dívat jako na pozdržení vyhodnocení výrazu až do okamžiku jeho zavolání, zatímco na interpolaci se můžeme dívat jako na přesně opačnou operace – vynucení vyhodnocení označené části výrazu.
11. Makrosystém programovacího jazyka Julia
Makrosystém programovacího jazyka Julia je založen na tom, že makra jsou spouštěna již ve chvíli, kdy je kód parsován, tedy předtím, než je vůbec spuštěn interpretrem. To znamená, že makra mohou do kódu (resp. do jeho AST) vkládat další symboly, modifikovat symboly, které do původní podoby kódu zapsal uživatel atd. Makro se deklaruje podobným způsobem jako funkce, samozřejmě s tím rozdílem, že se používá klíčové slovo macro. Musíme se však uvědomit, že parametrem či parametry makra není nějaký již vyhodnocený výraz (typicky konstanta), ale zpracovaný AST:
julia& macro sayhello(name) return :( println("Hello, ", $name) ) end
Použití makra je od volání funkcí odlišné a lze použít dvě podoby:
julia& @sayhello "Root.cz" Hello, Root.cz julia& @sayhello("Root.cz") Hello, Root.cz
Pokud by makro mělo více parametrů, budou se v prvním případě oddělovat mezerami, ve druhém čárkami (jako u funkcí).
Další makro můžeme použít v případě, kdy potřebujeme najít chybu v nějakém výrazu, nebo pouze zjišťujeme, jaké výrazy se v programu používají. Makro nejdříve vypíše tvar výrazu a poté vrátí výsledek jeho vyhodnocení:
julia& macro debug(expression) println("Evaluating: ", expression) return eval(expression) end julia& @debug 1+2*3 Evaluating: 1 + 2 * 3 7 julia& @debug(1+2*3) Evaluating: 1 + 2 * 3 7
Prozatím poslední makro, které si ukážeme, lze použít pro aserci. Povšimněte si použití citace i interpolace:
julia& macro assert(ex) return :( $ex ? nothing : throw(AssertionError($(string(ex)))) ) end
Příklad použití:
julia& assert(1<2) julia& assert(1>2) ERROR: AssertionError: in assert at error.jl:38
Složitější makra si popíšeme a ukážeme příště.
12. Odkazy na Internetu
- S-expression (Wikipedia)
https://en.wikipedia.org/wiki/S-expression - S-Expressions (Rosetta Code)
http://rosettacode.org/wiki/S-Expressions - Metaprogramming (Julia)
http://julia.readthedocs.io/en/latest/manual/metaprogramming/ - Introducing Julia/Metaprogramming
https://en.wikibooks.org/wiki/Introducing_Julia/Metaprogramming - Tutorial for the Common Lisp Loop Macro
http://www.ai.sri.com/pkarp/loop.html - Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html - Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html - Clojure Macro Tutorial (Part III: Syntax Quote)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html - Clojure Macros and Metaprogramming
http://clojure-doc.org/articles/language/macros.html - 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 - CS 2101 Parallel Computing with Julia
https://www.coursehero.com/file/11508091/CS-2101-Parallel-Computing-with-Julia/ - Julia By Example
https://samuelcolvin.github.io/JuliaByExample/ - Tasks and Parallel Computing
http://docs.julialang.org/en/release-0.4/stdlib/parallel/ - clock(3) – Linux man page
http://linux.die.net/man/3/clock - rand_r(3) – Linux man page
http://linux.die.net/man/3/rand_r - atan2(3) – Linux man page
http://linux.die.net/man/3/atan2 - Calling C and Fortran Code
http://docs.julialang.org/en/release-0.4/manual/calling-c-and-fortran-code/?highlight=symbol - Array Programming
https://en.wikipedia.org/wiki/Array_programming - Discovering Array Languages
http://archive.vector.org.uk/art10008110 - no stinking loops – Kalothi
http://www.nsl.com/ - Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
http://www.vector.org.uk/ - APL Interpreters
http://www.vector.org.uk/?area=interpreters - APL_(programming_language
http://en.wikipedia.org/wiki/APL_(programming_language - APL FAQ
http://www.faqs.org/faqs/apl-faq/ - APL FAQ (nejnovější verze)
http://home.earthlink.net/~swsirlin/apl.faq.html - A+
http://www.aplusdev.org/ - APLX
http://www.microapl.co.uk/ - FreeAPL
http://www.pyr.fi/apl/index.htm - J: a modern, high-level, general-purpose, high-performance programming language
http://www.jsoftware.com/ - K, Kdb: an APL derivative for Solaris, Linux, Windows
http://www.kx.com - openAPL (GPL)
http://sourceforge.net/projects/openapl - Parrot APL (GPL)
http://www.parrotcode.org/ - Learning J (Roger Stokes)
http://www.jsoftware.com/help/learning/contents.htm - Rosetta Code
http://rosettacode.org/wiki/Main_Page - Why APL
http://www.acm.org/sigapl/whyapl.htm - Introducing Julia/Functions
https://en.wikibooks.org/wiki/Introducing_Julia/Functions - Functions (Julia documentation)
http://docs.julialang.org/en/release-0.4/manual/functions/ - Evaluate binomial coefficients
http://rosettacode.org/wiki/Evaluate_binomial_coefficients - Ackermann function
http://rosettacode.org/wiki/Ackermann_function - Julia (front page)
http://julialang.org/ - Julia – dokumentace
http://docs.julialang.org/en/release-0.4/ - Julia – repositář na GitHubu
https://github.com/JuliaLang/julia - Julia (programming language)
https://en.wikipedia.org/wiki/Julia_%28programming_language%29 - IJulia
https://github.com/JuliaLang/IJulia.jl - Introducing Julia
https://en.wikibooks.org/wiki/Introducing_Julia - Julia: the REPL
https://en.wikibooks.org/wiki/Introducing_Julia/The_REPL - Month of Julia
https://github.com/DataWookie/MonthOfJulia - Learn X in Y minutes (where X=Julia)
https://learnxinyminutes.com/docs/julia/ - New Julia language seeks to be the C for scientists
http://www.infoworld.com/article/2616709/application-development/new-julia-language-seeks-to-be-the-c-for-scientists.html - Julia: A Fast Dynamic Language for Technical Computing
http://karpinski.org/publications/2012/julia-a-fast-dynamic-language - The LLVM Compiler Infrastructure
http://llvm.org/ - Julia: benchmarks
http://julialang.org/benchmarks/ - Type system
https://en.wikipedia.org/wiki/Type_system - Half-precision floating-point format
https://en.wikipedia.org/wiki/Half-precision_floating-point_format