Obsah
1. Programovací jazyk Julia: volání funkcí naprogramovaných v C či ve Fortranu
3. Volání nativních funkcí bez parametrů a problematika návratových typů
4. Předání parametrů do nativních funkcí
5. Práce s řetězci – specifikace ukazatele a zpětná konverze řetězce
6. Vytvoření vlastní demonstrační sdílené knihovny
7. Volání funkcí ze sdílené knihovny, předání parametrů, zpracování výsledků
8. Druhá demonstrační sdílená knihovna s funkcí zpracovávající pole
9. Předání pole z jazyka Julia do nativní knihovny
10. Segfault a další typické problémy
11. Globální proměnné v nativní knihovně
12. Přístup ke globálním proměnným přes funkce unsafe_load a unsafe_store!
13. Repositář s demonstračními zdrojovými kódy nativních knihoven
1. Programovací jazyk Julia: volání funkcí naprogramovaných v C či Fortranu
Běhové prostředí programovacího jazyka Julia podporuje kooperaci s nativními knihovnami naprogramovanými typicky v programovacích jazycích C a (možná ještě častěji) ve Fortranu. To ve skutečnosti není nic překvapivého, protože mnoho algoritmů určených pro zpracování numerických dat bylo naprogramováno, odladěno a optimalizováno právě ve Fortranu, takže by bylo kontraproduktivní se snažit o přepis těchto algoritmů do pomalejšího a prozatím (?) méně rozšířeného jazyka Julia. Ostatně přesně tímto způsobem jsou do jazyka Julia přidány algoritmy pro lineární algebru – ve skutečnosti totiž bylo pouze vytvořeno rozhraní ke knihovně LAPACK (Linear Algebra Package) s tím, že toto rozhraní zajišťuje případné datové konverze mezi vektory a maticemi jazyka Julia a týmiž strukturami implementovanými ve Fortranu. Dnes si ukážeme, jak lze volat funkce uložené v prakticky libovolné nativní sdílené knihovně (.so, .dll).
2. Funkce ccall
Pro volání nativních funkcí přímo z programovacího jazyka Julia, resp. přesněji řečeno ze skriptů naprogramovaných v Julii, je určena funkce nazvaná ccall (zkratka operace C-call). Této funkci se předávají následující parametry:
- Dvojice obsahující jméno volané funkce a jméno knihovny. Dvojice není nic jiného než n-tice se dvěma prvky. Ve skriptech se n-tice se zapisuje s využitím kulatých závorek.
- Typ návratové hodnoty, například UInt8, Int32, Float32 či Float64
- Následuje n-tice obsahující popis typů parametrů nativní funkce (pozor na to, že n-tice s jedním prvkem musí končit čárkou)
- Vlastní parametry, které se nativní funkci mají předat. V případě potřeby se Julia pokusí o konverzi typů, to však není ve všech případech možné ani bezpečné.
Pro ilustraci řešení celé problematiky volání nativních funkcí přímo z programovacího jazyka Julia si nejprve ukážeme, jakým způsobem se volají ty nativní funkce, které neakceptují žádné parametry. Typickým příkladem takové funkce, který je ostatně použit i v originální dokumentaci programovacího jazyka Julia, je funkce pojmenovaná clock, která vrací čas procesoru v jednotkách, jež lze na sekundy v případě potřeby převést podělením konstantou CLOCKS_PER_SEC (tato konstanta má podle POSIXu hodnotu 1000000, nicméně se ještě stále můžete setkat se systémy, v nichž je tato hodnota odlišná). Funkce clock se poměrně často používá pro změření doby trvání nějaké operace – stačí zjistit počet „tiků“ procesoru před měřenou operací, druhý počet „tiků“ procesoru po této operaci a podělit vypočtený rozdíl výše zmíněnou konstantou CLOCKS_PER_SEC. Funkce clock je samozřejmě podrobně popsána v manuálových stránkách, z nichž zjistíme jak parametry (žádné nejsou), tak i typ návratové hodnoty:
man 3 clock
CLOCK(3) Linux Programmer's Manual CLOCK(3) NAME clock - determine processor time SYNOPSIS #include <time.h> clock_t clock(void); DESCRIPTION The clock() function returns an approximation of processor time used by the program. ... ... ...
3. Volání nativních funkcí bez parametrů a problematika návratových typů
V dokumentaci k programovacímu jazyku Julia je ukázán způsob volání výše zmíněné nativní funkce clock. Příkaz pro vyvolání je popsán následovně – volá se funkce se jménem „clock“ uložená ve sdílené knihovně nazvané „libc“, návratovým typem je 32bitové celé číslo a funkce neakceptuje žádné parametry:
julia> ccall( ("clock", "libc"), Int32, ()) 1374123
Alternativně lze jméno volané nativní funkce zadat formou symbolu:
julia> ccall( (:clock, "libc"), Int32, ()) 1799851
Totéž platí i pro jméno knihovny:
julia> ccall( (:clock, :libc), Int32, ()) 1999851
Ve skutečnosti se však při použití návratové hodnoty typu Int32 brzy dostaneme do problémů, protože typ clock_t má na 64bitových systémech šířku 64 bitů, o čemž se můžete v céčku snadno přesvědčit použitím operátoru sizeof:
#include <time.h> #include <stdio.h> int main(void) { printf("%zu\n", sizeof(clock_t)); return 0; }
Na 64bitových systémech sice použití typu Int32 nepovede k vypsání chybového hlášení, protože jazyk Julia nemá možnost takovou kontrolu provést, ale pro správné chování je nutné použít:
julia> ccall( (:clock, "libc"), Int64, ()) 1441903
Podobným způsobem lze zavolat například další standardní funkci rand s následujícím popisem v manuálové stránce:
man 3 rand
RAND(3) Linux Programmer's Manual RAND(3) NAME rand, rand_r, srand - pseudo-random number generator SYNOPSIS #include <stdlib.h> int rand(void); ... ... ...
Podle konkrétní architektury (32bit, 64bit) použijte jedno z následujících volání:
julia> ccall( ("rand", "libc"), Int32, ()) 1804289383 julia> ccall( ("rand", "libc"), Int64, ()) 1804289383
Vygenerování desetiprvkové sekvence pseudonáhodných čísel s využitím nativní funkce rand vypadá takto:
julia> for i in 1:10 println(ccall( ("rand", "libc"), Int64, ())) end 1102520059 2044897763 1967513926 1365180540 1540383426 304089172 1303455736 35005211 521595368 294702567
4. Předání parametrů do nativních funkcí
Poměrně často se setkáme s nutností zavolat nativní funkci, které se musí předat nějaké parametry. Pro jednoduchost si ukážeme volání takových funkcí na příkladu funkce nazvané abs, která je opět součástí standardní céčkovské knihovny (na Linuxu je tato funkce uložena v souboru libc.so.verze, kterou najdete většinou v adresáři /lib/{architektura}-linux-gnu/). Hlavička funkce abs naznačuje, že se akceptuje celé číslo a návratovou hodnotou je absolutní hodnota tohoto čísla (samozřejmě kromě absolutní hodnoty nejmenšího záporného čísla, protože tato absolutní hodnota překračuje rozsah typu int):
ABS(3) Linux Programmer's Manual ABS(3) NAME abs, labs, llabs, imaxabs - compute the absolute value of an integer SYNOPSIS #include <stdlib.h> int abs(int j); long int labs(long int j); long long int llabs(long long int j);
Podívejme se nyní, jakým způsobem se tato nativní funkce zavolá z programovacího jazyka Julia. Pozor si musíme dát především na způsob zápisu typů parametrů při volání ccall, protože nestačí pouze napsat (Int64), neboť tento zápis neodpovídá jednoprvkové n-tici, ale pouze typu uzavřenému v závorkách. Korektní zápis jednoprvkové n-tice vypadá takto: (Int64,); ostatně podobně je tomu i v Pythonu. Volání výše zmíněné nativní funkce abs tedy bude na 64bitovém systému vypadat následovně:
julia> ccall( ("abs", "libc"), Int64, (Int64,), 42) 42 julia> ccall( ("abs", "libc"), Int64, (Int64,), -42) 42
Podobným způsobem je možné zavolat nativní funkci nazvanou atan2, která akceptuje dvě reálné hodnoty typu double, což je typ, který v jazyce Julia odpovídá typu Float64. Pozor je zapotřebí dát na to, že tuto funkci nenalezneme v knihovně libc.so:
julia> ccall( ("atan2", "libc"), Float64, (Float64,Float64), 0, -1) ERROR: ccall: could not find function atan2 in library libc in anonymous at no file
ale v knihovně libm.so. Korektní volání tedy vypadá následovně:
julia> ccall( ("atan2", "libm"), Float64, (Float64,Float64), 0, -1) 3.141592653589793
5. Práce s řetězci – specifikace ukazatele a zpětná konverze řetězce
Mnoho nativních funkcí vyžaduje jako své parametry řetězce, resp. přesněji řečeno céčkové řetězce, v nichž jsou zapsány jednotlivé bajty, celý řetězec je ukončen bajtem s hodnotou nula a řetězec je předán nikoli hodnotou, ale referencí (adresou jeho prvního znaku). Mezi céčkovými řetězci a řetězci používanými v jazyku Julia existuje několik rozdílů (v jazyku Julia jsou řetězce neměnné, podporovány jsou všechny Unicode znaky apod.), ovšem při předávání řetězců pomocí reference (adresy) postačuje u řetězce použít typ Cstring popř. Ptr{UInt8}. V případě použití typu Cstring je provedena kontrola, zda řetězec předávaný z jazyka Julia neobsahuje bajty s nulou, pokud však použijete Ptr{UInt8}, tato kontrola se neprovede, což je u některých funkcí nezbytné (práce s binárními daty apod.).
Ukažme si nyní, jakým způsobem lze zavolat funkci nazvanou system, která jako svůj jediný parametr akceptuje řetězec představující příkaz shellu (s případnými parametry atd.), který se má spustit (opět pozor na způsob zápisu jednoprvkové n-tice). Návratovou hodnotou této funkce je návratový kód spouštěného příkazu (nikoli tedy například jméno uživatele v případě volání „whoami“):
julia> ccall( ("system", "libc"), Int32, (Ptr{UInt8},), "whoami") tester 0
Můžeme samozřejmě spustit i složitější příkaz:
julia> ccall( ("system", "libc"), Int32, (Ptr{UInt8},), "ls -1") a.out julia julia-debug libtest1.so libtest2.so test1.c test1.o test2.c test2.o 0
Popř. si nechat na standardní výstup vypsat aktuální datum a čas:
julia> ccall( ("system", "libc"), Int32, (Ptr{UInt8},), "date") Pá čen 24 22:54:25 CEST 2016 0
Opačná operace, tedy přečtení řetězce vráceného nativní funkcí, je nepatrně složitější. Pro příklad si vyzkoušejme nativní funkci nazvanou getenv:
GETENV(3) Linux Programmer's Manual GETENV(3) NAME getenv, secure_getenv - get an environment variable SYNOPSIS #include <stdlib.h> char *getenv(const char *name);
První pokus o přečtení proměnné prostředí nazvané PATH nedopadne přesně podle našich představ, a to z toho důvodu, že se pouze zobrazí hodnota ukazatele (ostatně my jsme skutečně vyžadovali získání hodnoty typu Ptr{UInt8}):
julia> ccall((:getenv, "libc"), Ptr{UInt8}, (Ptr{UInt8},), "PATH") Ptr{UInt8} @0x00007fff14ae9c5c
Konverzi na skutečný řetězec jazyka Julia z céčkového řetězce zajišťuje funkce bytestring:
julia> bytestring( ccall((:getenv, "libc"), Ptr{UInt8}, (Ptr{UInt8},), "PATH")) "/home/tester/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/tester/bin"
Poznámka: ve skutečnosti má funkce bytestring i opačný význam v závislosti na typu předaného parametru.
6. Vytvoření vlastní demonstrační sdílené knihovny
Nyní se podívejme na způsob realizace vlastní nativní knihovny, jejíž funkce bude možné zavolat přímo ze skriptů programovacího jazyka Julia. Naše demonstrační knihovna bude obsahovat tři jednoduché funkce – první funkce neakceptuje žádné parametry a vrací číslo typu integer, druhá funkce akceptuje dva celočíselné parametry a taktéž vrací integer a konečně třetí funkce akceptuje dva parametry typu double a stejného typu je i její návratová hodnota:
int answer(void) { return 42; } int add_integers(int x, int y) { return x+y; } double add_doubles(double x, double y) { return x+y; }
Při překladu knihovny do objektového kódu je nutné použít parametr -fPIC, aby byl vygenerovaný strojový kód nezávislý na absolutních adresách, na nichž je uložen:
gcc -Wall -fPIC -ansi -c test1.c
Výsledná sdílená knihovna se následně vygeneruje linkerem, kterému se předá požadované jméno knihovny:
gcc -shared -Wl,-soname,libtest1.so -o libtest1.so test1.o
Výsledkem by měl být soubor pojmenovaný libtest1.so.
7. Volání funkcí ze sdílené knihovny, předání parametrů, zpracování výsledků
Před pokusem o zavolání některé funkce z právě vytvořené sdílené knihovny je nutné nastavit proměnnou prostředí LD_LIBRARY_PATH takovým způsobem, aby tato proměnná ukazovala na adresář se sdílenými knihovnami. Pro jednoduchost předpokládejme použití aktuálního adresáře, v němž je současně uložen i spustitelný soubor s interpretrem jazyka Julia. Proměnná prostředí LD_LIBRARY_PATH se nastaví takto:
tester ~/temp/julia-2ac304dfba/bin $ export LD_LIBRARY_PATH=.
Po tomto nastavení již spustíme interpret s interaktivní smyčkou REPL:
tester ~/temp/julia-2ac304dfba/bin $ ./julia _ _ _ _(_)_ | A fresh approach to technical computing (_) | (_) (_) | Documentation: http://docs.julialang.org _ _ _| |_ __ _ | Type "?help" for help. | | | | | | |/ _` | | | | |_| | | | (_| | | Version 0.4.5 (2016-03-18 00:58 UTC) _/ |\__'_|_|_|\__'_| | Official http://julialang.org/ release |__/ | x86_64-unknown-linux-gnu
Následně se můžeme pokusit zavolat první funkci, která neakceptuje žádné parametry a vrací hodnotu typu integer:
julia> ccall( ("answer", "libtest1"), Int32, ()) 42
Fungovat bude i prosté:
julia> ccall( ("answer", "libtest1"), Int, ()) 42
Součet dvou celých čísel, který zajišťuje nativní funkce pojmenovaná add_integers, provedeme takto:
julia> ccall( ("add_integers", "libtest1"), Int, (Int, Int), 1, 2) 3
alternativně též:
julia> ccall( ("add_integers", "libtest1"), Int32, (Int32, Int32), 10, 20) 30
Poslední funkce pojmenovaná add_doubles sečte dvě čísla typu double, resp. v typovém systému jazyka Julia dvě čísla typu Float64:
julia> ccall( ("add_doubles", "libtest1"), Float64, (Float64, Float64), 1.2, 3.4) 4.6
Některé konverze se provádí automaticky (zde celé číslo na číslo typu double):
julia> ccall( ("add_doubles", "libtest1"), Float64, (Float64, Float64), 1, 2) 3
Další konverze se však (automaticky) neprovádí, což je ostatně jen dobře, protože o tyto konverze musí uživatel explicitně požádat:
julia> ccall( ("add_integers", "libtest1"), Int, (Int, Int), 1.2, 2) ERROR: InexactError() in anonymous at no file
8. Druhá demonstrační sdílená knihovna s funkcí zpracovávající pole
Kromě předávání běžných skalárních hodnot (celé číslo, číslo s plovoucí řádovou čárkou atd.) a řetězců se velmi často setkáme s nutností předat do volaných nativních funkcí pole. Ve skutečnosti se nejedná o žádnou složitou operaci a pokud se dodrží správné datové typy, rozměry, dimenze atd., je předávání polí do nativních funkcí bezpečné. Vyzkoušejme si práci s poli na vlastní nativní sdílené knihovně s jedinou funkcí, která spočítá součet všech prvků v poli. Vzhledem k tomu, jakým způsobem s poli pracuje jazyk C (jedná se o pouhý blok v paměti bez dalších metainformací, tj. bez mezí, délky atd.), je nutné do funkce předat i délku pole:
double sum(double *array, int length) { double *p = array; double result = 0; int i; for (i=0; i<length; i++) { result += *p++; } return result; }
Zdrojový kód přeložíme stejným způsobem, jako tomu bylo u první demonstrační knihovny:
gcc -Wall -fPIC -ansi -c test2.c
Následuje druhá operace volající linker, který sdílenou knihovnu vygeneruje:
gcc -shared -Wl,-soname,libtest2.so -o libtest2.so test2.o
Výsledkem činnosti linkeru by měl být soubor pojmenovaný libtest2.so.
Před spuštěním interpretru opět musíme nastavit proměnnou prostředí LD_LIBRARY_PATH (pokud samozřejmě již nezůstala nastavená z předchozích pokusů):
tester ~/temp/julia-2ac304dfba/bin $ export LD_LIBRARY_PATH=.
9. Předání pole z jazyka Julia do nativní knihovny
Z předchozích částí tohoto seriálu již víme, jakým způsobem se v programovacím jazyce Julia vytváří pole s prvky majícími požadovaný typ. Naše nativní sdílená knihovna obsahuje funkci akceptující typ double, což v jazyce Julia odpovídá typu Float64. Vytvořme si tedy pole se třemi prvky tohoto typu. Povšimněte si, že se prvky nijak neinicializují a mohou obsahovat náhodné hodnoty:
julia> a=Array{Float64}(3) 3-element Array{Float64,1}: 6.93643e-310 6.936e-310 6.936e-310
Dále toto pole naplníme hodnotami, například takto:
julia> a[1]=1 1 julia> a[2]=2 2 julia> a[3]=3 3
Nyní již je vše připraveno pro zavolání nativní funkce, která sečte prvky pole:
julia> ccall( ("sum", "libtest2"), Float64, (Ptr{Float64}, Int), a, 3) 6.0
Pole samozřejmě můžeme vytvořit i dalšími konstruktory, například funkcemi zeros, ones či fill. Podívejme se například na nepřímý důkaz, že hodnotu 0.1 nelze typem double přesně reprezentovat:
julia> b=fill(0.1, 10) 10-element Array{Float64,1}: 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
Součet deseti prvků s hodnotou „přibližně 0.1“ nebude roven jedničce:
julia> ccall( ("sum", "libtest2"), Float64, (Ptr{Float64}, Int), b, 10) 0.9999999999999999
10. Segfault a další typické problémy
Interpret programovacího jazyka Julia při volání nativních funkcí nepoužívá žádné speciální techniky pro kontrolu předávaných parametrů ani jejich typů (a už vůbec ne kontrolu mezí polí). Je tedy velmi snadné dosáhnout pádu interpretru způsobeného například porušením ochrany paměti v nativní knihovně apod. Příklad je jednoduchý – specifikujeme pole nesprávného typu a současně namísto skutečného pole předáme pouze jedinou skalární hodnotu. Výsledek bude poněkud depresivní :-):
julia> ccall( ("sum", "libtest2"), Float64, (Ptr{Int}, Int), 0, 10) WARNING: convert(::Type{Ptr}, ::Int64) methods should be converted to be methods of unsafe_convert in depwarn at deprecated.jl:73 [inlined code] from deprecated.jl:418 in unsafe_convert at no file:0 in anonymous at no file while loading no file, in expression starting on line 0 signal (11): Segmentation fault sum at ./libtest2.so (unknown line) anonymous at no file:0 unknown function (ip: 0x7fb03b9aef73) jl_toplevel_eval_in at /home/tester/temp/julia-2ac304dfba/bin/../lib/julia/libjulia.so (unknown line) eval_user_input at REPL.jl:62 jlcall_eval_user_input_21168 at (unknown line) jl_apply_generic at /home/tester/temp/julia-2ac304dfba/bin/../lib/julia/libjulia.so (unknown line) anonymous at REPL.jl:92 unknown function (ip: 0x7fb03b9a08f4) unknown function (ip: (nil)) Segmentation fault
Je nutné si uvědomit, že mezi nativním kódem a skriptem naprogramovaným v jazyce Julia leží pouze tenká vrstva rozhraní reprezentovaná funkcí ccall, takže veškeré typové kontroly musí provádět uživatel. To je daň, která se platí za použití rychlého rozhraní.
11. Globální proměnné v nativní knihovně
Vytvořme si třetí a současně i poslední nativní sdílenou knihovnu. Tentokrát v této knihovně nebudou uloženy žádné funkce, ale pouze čtveřice globálních proměnných:
int i = 0; int answer = 42; float pi_f = 3.1415f; double pi_d = 3.1415;
Průběh překladu a slinkování již známe:
gcc -Wall -fPIC -ansi -c test3.c gcc -shared -Wl,-soname,libtest3.so -o libtest3.so test3.o
12. Přístup ke globálním proměnným přes funkce unsafe_load a unsafe_store!
Ke globálním proměnným lze přistupovat s využitím funkce nazvané cglobal, která se v mnoha ohledech podobá funkci ccall, protože i zde musíme v n-tici předat jméno proměnné a jméno knihovny. Druhým parametrem je typ globální proměnné. Návratovou hodnotou této funkce je však ukazatel:
julia> cglobal(("answer", "libtest3"), Int32) Ptr{Int32} @0x00007fd1a9acb030 julia> cglobal(("pi_f", "libtest3"), Float32) Ptr{Float32} @0x00007fd1a9acb034 julia> cglobal(("pi_d", "libtest3"), Float64) Ptr{Float64} @0x00007fd1a9acb038 julia> cglobal(("i", "libtest3"), Int32) Ptr{Int32} @0x00007fd1a9acb044
Načtení hodnoty, na níž ukazuje vrácený ukazatel, zajišťuje funkce nazvaná unsafe_load. Přečtení hodnoty globální proměnné answer tedy vypadá následovně:
julia> answer_ptr = cglobal(("answer", "libtest3"), Int32) Ptr{Int32} @0x00007f99fd7d2030 julia> unsafe_load(answer_ptr) 42
Pro zápis se používá funkce nazvaná unsafe_store! (i s vykřičníkem na konci). Změna globální proměnné i vypadá takto:
julia> i_ptr = cglobal(("i", "libtest3"), Int32) Ptr{Int32} @0x00007f99fd7d2044 julia> unsafe_load(i_ptr) 0 julia> unsafe_store!(i_ptr, 1000) Ptr{Int32} @0x00007f99fd7d2044 julia> unsafe_load(i_ptr) 1000
13. Repositář s demonstračními zdrojovými kódy nativních knihoven
Všechny tři demonstrační zdrojové kódy, které se mají přeložit do nativních sdílených knihoven, byly uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/presentations/:
# | Soubor | Popis | Adresa |
---|---|---|---|
1 | test1.c | zdrojový text pro knihovnu se třemi funkcemi | https://github.com/tisnik/presentations/blob/master/julia/test1.c |
2 | test2.c | zdrojový text pro knihovnu s funkcí akceptující pole | https://github.com/tisnik/presentations/blob/master/julia/test2.c |
3 | test3.c | zdrojový text pro knihovnu s globálními proměnnými | https://github.com/tisnik/presentations/blob/master/julia/test3.c |
14. Odkazy na Internetu
- 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 - Introducing Julia/Metaprogramming
https://en.wikibooks.org/wiki/Introducing_Julia/Metaprogramming - 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