Obsah
1. Programovací jazyk Julia: typový systém, funkce a metody
2. K čemu jsou typy přiřazeny? Zjištění typu hodnoty
3. Základní datové typy v programovacím jazyku Julia
4. Zjištění minimálních a maximálních hodnot základních datových typů, zjištění přesnosti
5. Anotace se specifikací typu
6. Kontrola typů za běhu programu (aserce)
8. Vytvoření vlastního typu se specifikací jeho bitové šířky
10. Konstruktory pro kompozitní datové typy
11. Neměnitelné hodnoty (immutable types)
13. Funkce a metody v programovacím jazyku Julia
1. Programovací jazyk Julia: typový systém, funkce a metody
V úvodním článku tohoto seriálu jsme se seznámili s některými základními vlastnostmi programovacího jazyka Julia. Připomeňme si, že se jedná o poměrně mladý programovací jazyk, který je primárně určený pro tvorbu jednoúčelových skriptů, ale i rozsáhlých aplikací v oblastech numerické matematiky a statistiky, s čímž poměrně úzce souvisí podpora pro snadné použití nativních knihoven naprogramovaných v jazycích Fortran, C či C++. Programovací jazyk Julia v těchto oblastech konkuruje především projektům R či GNU Octave, částečně taktéž dvojici knihoven Numpy+SciPy společně s Matplotlib (může ovšem někde nahradit i komerční produkty typu Matlab). Julia, ostatně jako většina nově vznikajících jazyků, reflektuje aktuální směr vývoje mikroprocesorů, takže podporuje i paralelní výpočty popř. i distribuci výpočtů mezi několik (i virtuálních) strojů.
Poměrně důležitou součástí programovacího jazyka Julia je jeho typový systém, který je sice dynamický, ovšem v případě potřeby je možné u libovolné hodnoty, parametru funkce či její návratové hodnoty určit její typ způsobem, který známe ze staticky typovaných jazyků. Typový systém je navržen takovým způsobem, že ti programátoři, kteří se jím nechtějí či nepotřebují zabývat, ho mohou prakticky zcela ignorovat a nechat samotný jazyk Julia, ať provádí dynamické určování typů automaticky. Na druhou stranu však znalost typového systému a způsobu deklarace nových typů (včetně typů parametrických) může vést jak k tvorbě efektivnějšího kódu (jak z hlediska nároků na operační paměť, tak i rychlosti výpočtů), tak i k možnostem kontroly chování aplikace v čase jejího běhu (runtime) či k možnosti deklarace chování funkcí na základě typů předávaných parametrů (takové funkce jsou pak realizovány takzvanými metodami, přičemž každá metoda předepisuje chování funkce pro určitou množinu typů parametrů). Právě možnost tvorby vlastních uživatelských typů představuje jednu ze silných zbraní jazyka Julia, i když je samozřejmě možné, že ne všem bude tato vlastnost vyhovovat.
2. K čemu jsou typy přiřazeny? Zjištění typu hodnoty
V programovacím jazyce Julia je informace o typu přiřazena vždy k hodnotě, nikoli nutně k proměnné. Proměnná totiž v tom nejjednodušším případě představuje pouhou vazbu mezi jménem proměnné a konkrétní hodnotou. To mj. znamená, že jedna proměnná může mít v různém čase přiřazenu hodnotu s odlišným datovým typem. Na druhou stranu se ovšem k proměnné může deklarovat aserce kontrolující typ hodnoty, což je systém podobný staticky typovaným programovacím jazykům. Tento koncept je sice logický a pochopitelný, ovšem může způsobovat problémy těm programátorům, kteří přechází například z jazyka Java, kde je koncept typů vlastně převrácený – typ je deklarací přiřazen k proměnným (atributům …) a hodnota přiřazovaná do takové proměnné je konvertována na požadovaný typ a teprve pokud se konverze nedá automaticky provést podle pravidel popsaných ve specifikaci jazyka, je o tom programátor informován.
Pro zjištění typu libovolné hodnoty je možné použít funkci nazvanou typeof():
julia> typeof(42) Int64 julia> typeof(1//3) Rational{Int64} julia> typeof(pi) Irrational{:π} julia> typeof(1.2) Float64 julia> typeof(1+2im) Complex{Int64} julia> typeof(1.2+3im) Complex{Float64}
Samozřejmě lze tutéž funkci použít i na výrazy, přičemž daný výraz je vyhodnocen (vypočten) a následně je zjištěn typ výsledku. Povšimněte si automatických konverzí u výrazů, v nichž se vyskytují hodnoty různých datových typů:
julia> typeof(1+2) Int64 julia> typeof(1+2//3) Rational{Int64} julia> typeof(1.2+2//3) Float64 julia> typeof(sin(10)) Float64 julia> typeof(true) Bool julia> typeof(false) Bool
Typ se zachová i u hodnot typu nekonečno (kladné i záporné) nebo NaN:
julia> typeof(1/0) Float64 julia> typeof(-1/0) Float64 julia> typeof(0/0) Float64
Tutéž funkci je možné použít i pro proměnné, ovšem jak již víme z předchozího textu, nezjišťuje se typ proměnné (taková informace vlastně neexistuje), ale typ hodnoty spojené v daný okamžik se jménem proměnné:
julia> x=42 42 julia> typeof(x) Int64 julia> x=3.14 3.14 julia> typeof(x) Float64 julia> x=1//3 1//3 julia> typeof(x) Rational{Int64} julia> x=pi π = 3.1415926535897... julia> typeof(x) Irrational{:π}
3. Základní datové typy v programovacím jazyku Julia
Se základními datovými typy jsme se již částečně setkali minule, takže si nyní pouze popišme základní vlastnosti těchto typů:
Jméno | Znaménko? | Inf | NaN | Šířka(b) | Nejmenší hodnota | Nejvyšší hodnota |
---|---|---|---|---|---|---|
Bool | ne | ne | ne | 8 | false | true |
Int8 | ano | ne | ne | 8 | –27 | 27 – 1 |
UInt8 | ne | ne | ne | 8 | 0 | 28 – 1 |
Int16 | ano | ne | ne | 16 | –215 | 215 – 1 |
UInt16 | ne | ne | ne | 16 | 0 | 216 – 1 |
Int32 | ano | ne | ne | 32 | –231 | 231 – 1 |
UInt32 | ne | ne | ne | 32 | 0 | 232 – 1 |
Int64 | ano | ne | ne | 64 | –263 | 263 – 1 |
UInt64 | ne | ne | ne | 64 | 0 | 264 – 1 |
Int128 | ano | ne | ne | 128 | –2127 | 2127 – 1 |
UInt128 | ne | ne | ne | 128 | 0 | 2128 – 1 |
Float16 | ano | ano | ano | 16 | –65504 | 65504 |
Float32 | ano | ano | ano | 32 | –3.402823 × 1038 | 3.402823 × 1038 |
Float64 | ano | ano | ano | 64 | –1.7 × 10308 | 1.7 × 10308 |
Mezi složené datové typy patří Complex64 (dvě hodnoty typu Float32) a Complex128 (dvě hodnoty typu Float64).
4. Zjištění minimálních a maximálních hodnot základních datových typů, zjištění přesnosti
Pro zjištění minimálních a maximálních reprezentovatelných hodnot pro jednotlivé numerické datové typy slouží funkce nazvané typemin() a typemax():
julia> typemin(Int8),typemin(Int16),typemin(Int32),typemin(Int64) (-128,-32768,-2147483648,-9223372036854775808) julia> typemax(Int8),typemin(Int16),typemin(Int32),typemin(Int64) (127,-32768,-2147483648,-9223372036854775808) julia> typemin(UInt8),typemin(UInt16),typemin(UInt32),typemin(UInt64) (0x00,0x0000,0x00000000,0x0000000000000000) julia> typemax(UInt8),typemax(UInt16),typemax(UInt32),typemax(UInt64) (0xff,0xffff,0xffffffff,0xffffffffffffffff)
Povšimněte si, že se u typů s plovoucí řádovou čárkou (floating point) vrací kladné a záporné nekonečno, nikoli minimální či maximální reprezentovatelná hodnota odlišná od nekonečna (v Javě například Double.MAX_VALUE):
julia> typemin(Float16), typemin(Float32), typemin(Float64) (-Inf16,-Inf32,-Inf) julia> typemax(Float16), typemax(Float32), typemax(Float64) (Inf16,Inf32,Inf)
Přesnost (vyjádřenou v počtu bitů mantisy) získáme funkcí precision():
julia> precision(Float16) 11 julia> precision(Float32) 24 julia> precision(Float64) 53
U celočíselných datových typů nelze tuto funkci použít.
5. Anotace se specifikací typu
V programovacím jazyce Julia je možné použít operátor :: („čtyřtečka“), který dokáže pro hodnotu, výraz, proměnnou, parametr funkce atd. zajistit, že bude skutečně použit vhodný datový typ. Pokud je tento operátor použit společně s proměnnou či parametrem funkce, jedná se vlastně o určitou formu statického typování, i když test, zda typy skutečně odpovídají, je proveden až v čase běhu aplikace (runtime). Podívejme se nejdříve na způsob použití tohoto operátoru pro kontrolu (či spíše aserci), zda je hodnota požadovaného typu:
julia> 1::Int 1 julia> 1::Float32 ERROR: TypeError: typeassert: expected Float32, got Int64 julia> 1::Bool ERROR: TypeError: non-boolean (Int64) used in boolean context julia> true::Bool true julia> true::Int ERROR: TypeError: typeassert: expected Int64, got Bool
Pozor na nutnost použití závorek u typu „zlomek“ či komplexní číslo:
julia> (1//3)::Rational 1//3 julia> (1+2im)::Complex 1 + 2im
6. Kontrola typů za běhu (aserce)
Kontrolu, zda je nějaká hodnota požadovaného datového typu, je možné provést i pro výrazy. Pokud je typ výsledku odlišný, vyhodí se výjimka, pokud však kontrola proběhne v pořádku, vrátí se hodnota vypočteného výrazu. Opět se podívejme na několik testů:
julia> (1+2)::Int8 ERROR: TypeError: typeassert: expected Int8, got Int64 julia> (1+2)::Int64 3 julia> (1+2)::Int 3 julia> (1+2)::Number 3 julia> (1//3+1//2)::Number 5//6 julia> (1//3+1//2)::Int ERROR: TypeError: typeassert: expected Int64, got Rational{Int64}
Kontrola se aplikuje ihned po její deklaraci, a to i pro existující hodnotu:
julia> z=0 0 julia> z::Int8 ERROR: TypeError: typeassert: expected Int8, got Int64 julia> z::Int64 0 julia> z=z/2.0 0.5 julia> z 0.5 julia> z::Int64 ERROR: TypeError: typeassert: expected Int64, got Float64
Ovšem mnohem důležitější je možnost deklarace typů parametrů metody. Jak si řekneme v dalším textu, může se s využitím několika metod implementovat funkce, jejíž chování může být pro různé typy předávaných parametrů odlišné. Nyní se tedy podívejme pouze na velmi jednoduchý příklad, kdy jazyk Julia kontroluje, zda metodě add skutečně předáváme dvě celá čísla se znaménkem:
julia> function add(x::Int, y::Int) return x+y end add (generic function with 1 method)
Metodu ihned otestujeme pro různé typy parametrů:
julia> add(1,2) 3 julia> add(1,2.0) ERROR: MethodError: `add` has no method matching add(::Int64, ::Float64) Closest candidates are: add(::Int64, ::Int64) julia> add(1,1//3) ERROR: MethodError: `add` has no method matching add(::Int64, ::Rational{Int64}) Closest candidates are: add(::Int64, ::Int64)
7. Abstraktní datové typy
V programovacím jazyce Julia je možné pracovat i s abstraktími datovými typy, které se nazývají abstraktní z toho důvodu, že není možné vytvářet hodnoty mající tento typ. K čemu jsou tedy abstraktní datové typy užitečné? Umožňují nám vybudovat hierarchii typů, která do určité míry odpovídá hierarchii tříd v objektově orientovaném programování. Každý datový typ (ať již typ konkrétní či abstraktní) totiž může mít svého předka a je tak možné (k čemuž se dostaneme později) například deklarovat metodu akceptující nikoli konkrétní datový typ, ale jeho abstrakci. Příkladem může být například metoda pro výpočet faktoriálu, která by neměla akceptovat libovolný vstup, ale pouze kladná celá čísla reprezentovaná konkrétními typy UInt8, UInt16, UInt32, UInt64 či UInt128. Ovšem celou situaci si můžeme zjednodušit a vytvořit metodu akceptující hodnotu abstraktního datového typu Unsigned, který zahrnuje výše zmíněné odvozené konkrétní datové typy.
Základní hierarchie datových typů programovacího jazyka Julia je sice (i když poněkud nepřesně) popsána v dokumentaci, ovšem pro libovolný typ lze získat informace o jeho předku či potomcích velmi snadno, a to s využitím funkcí nazvaných super() a subtypes(). Podívejme se na použití těchto funkcí:
julia> super(Int32) Signed julia> super(Signed) Integer julia> super(UInt8) Unsigned julia> super(Unsigned) Integer
Vidíme, že Integer je abstraktní typ, pod nějž spadají celá čísla se znaménkem i bez znaménka. Jaký je jeho nadtyp?:
julia> super(Integer) Real julia> super(Real) Number julia> super(Number) Any julia> super(Any) Any
Opět vidíme, že na vrcholu hierarchie je typ Any, který je použit implicitně, pokud nestanovíme jinak (tento typ má přibližně 220 podtypů, v závislosti na načtených knihovnách). Zajímavé jsou podtypy abstraktního typu Real:
julia> subtypes(Real) 4-element Array{Any,1}: AbstractFloat Integer Irrational{sym} Rational{T<:Integer}
I abstraktního typu Integer a AbstractFloat:
julia> subtypes(Integer) 4-element Array{Any,1}: BigInt Bool Signed Unsigned
julia> subtypes(AbstractFloat) 4-element Array{Any,1}: BigFloat Float16 Float32 Float64
8. Vytvoření vlastního typu se specifikací jeho bitové šířky
Zajímavé, mnohdy velmi užitečné a poněkud neobvyklé je, že uživatelé mohou vytvářet své vlastní datové typy odvozené od již existujících numerických typů, přičemž je možné specifikovat bitovou šířku nového datového typu. V současné verzi programovacího jazyka Julia musí být bitová šířka celočíselným násobkem osmi, což je však pochopitelné, především když si uvědomíme, jak jsou numerické hodnoty ukládány do operační paměti. Pro vytvoření nového datového typu uloženého na specifikovaný počet bitů se používá deklarace bitstype vypadající následovně:
bitstype počet_bitů jméno <: nadřazený_typ
Podívejme se, jak se vytvoří nový celočíselný datový typ bez znaménka, který používá 40 bitů (což je násobek osmi):
julia> bitstype 40 UInt40 <: Unsigned
Můžeme si vypsat základní informace o tomto typu:
help?> UInt40 search: UInt40 UInt4 UInt64 uint64 No documentation found. Summary: immutable UInt40 <: Unsigned
Potřebujete při výpočtech pracovat s celými čísly se znaménkem o šířce 256 bitů?:
julia> bitstype 256 Int256 <: Signed
Opět si můžeme vypsat základní informace o tomto typu:
help?> Int256 search: Int256 No documentation found. Summary: immutable Int256 <: Signed
Pro úplnost si ukažme, jak jsou deklarovány základní numerické datové typy (a boolean), s nimiž jsme se seznámili ve třetí kapitole:
bitstype 16 Float16 <: AbstractFloat bitstype 32 Float32 <: AbstractFloat bitstype 64 Float64 <: AbstractFloat bitstype 8 Bool <: Integer bitstype 32 Char bitstype 8 Int8 <: Signed bitstype 8 UInt8 <: Unsigned bitstype 16 Int16 <: Signed bitstype 16 UInt16 <: Unsigned bitstype 32 Int32 <: Signed bitstype 32 UInt32 <: Unsigned bitstype 64 Int64 <: Signed bitstype 64 UInt64 <: Unsigned bitstype 128 Int128 <: Signed bitstype 128 UInt128 <: Unsigned
9. Kompozitní datové typy
Pod názvem kompozitní datové typy si můžeme představit například struktury (struct) v jazyku C či záznamy (record) z Pascalu. Díky možnostem deklarace nových kompozitních datových typů bylo možné do jazyka Julia přidat nám již známá komplexní čísla (Complex64 a Complex128), ovšem uživatelé si mohou vytvořit libovolné množství dalších podobných typů. Podívejme se na několik jednoduchých příkladů – bude se jednat o takzvané kvaterniony (čtveřice), které jsou zobecněním komplexních čísel:
julia> type Quat r i j k end help?> Quat No documentation found. Summary: type Quat <: Any Fields: r :: Any i :: Any j :: Any k :: Any
Samozřejmě je možné specifikovat i typy jednotlivých položek kompozitního datového typu:
julia> type Quat256 r::Float32 i::Float32 j::Float32 k::Float32 end help?> Quat256 search: Quat256 No documentation found. Summary: type Quat256 <: Any Fields: r :: Float32 i :: Float32 j :: Float32 k :: Float32
10. Konstruktory pro kompozitní datové typy
Způsob vytvoření hodnoty některého ze základních datových typů samozřejmě známe – samotný zápis literálu představuje danou hodnotu (42 apod.). Ovšem v případě kompozitních datových typů je to nepatrně složitější, protože takový typ má větší množství položek, které je nutné inicializovat. Pro tento účel slouží takzvané konstruktory, které jsou vytvořeny automaticky. Přesněji řečeno jsou pro každý typ vytvořeny hned dva konstruktory, přičemž jeden volá funkci convert() pro konverzi svého parametru a druhý akceptuje takový počet parametrů, který odpovídá počtu položek datového typu. Na chvíli se vraťme k námi definovaným typům Quat a Quat256. Instance (objekty) těchto typů lze vytvořit snadno, ovšem povšimněte si toho, že pro stejné parametry získáme odlišné výsledky:
julia> x=Quat(1, 2//3, 3.14, 100000000) Quat(1,2//3,3.14,100000000) julia> y=Quat256(1, 2//3, 3.14, 100000000) Quat256(1.0f0,0.6666667f0,3.14f0,1.0f8)
V prvním případě je kompozitní datový typ Quat složen ze čtyř položek typu Any a proto se při konstrukci objektu neprovede žádná konverze. Ve druhém případě jsou položky typu Float32, takže se interpret pokusí o konverzi, která se zde povede. Ovšem někdy konverze není možná, takže interpret při kontrole typů zahlásí chybu:
julia> z=Quat("aa", "bb", "cc", "dd") Quat("aa","bb","cc","dd") julia> w=Quat256("aa", "bb", "cc", "dd") ERROR: MethodError: `convert` has no method matching convert(::Type{Float32}, ::ASCIIString) This may have arisen from a call to the constructor Float32(...), since type constructors fall back to convert methods. Closest candidates are: call{T}(::Type{T}, ::Any) convert(::Type{Float32}, ::Int8) convert(::Type{Float32}, ::Int16) ... in call at none:2
11. Neměnitelné hodnoty (immutable types)
K položkám kompozitních datových typů se přistupuje přes operátor tečky:
julia> x.r=100 100 julia> x Quat(100,2//3,3.14,100000000) julia> x.i 2//3
V některých případech nám však nemusí vyhovovat, že se položky dají měnit stylem proměnná.položka=hodnota. V takových případech je možné kompozitní datový typ deklarovat takovým způsobem, že položky typu nebudou měnitelné (tedy budou immutable). Je to velmi jednoduché, což je patrné z následujícího příkladu:
julia> immutable ImmutableQuat256 r::Float32 i::Float32 j::Float32 k::Float32 end julia> q=ImmutableQuat256(1/2, 1/3, 1/4, 1/5) ImmutableQuat256(0.5f0,0.33333334f0,0.25f0,0.2f0)
Pokus o přečtení položky je validní, ovšem pokus o její přepsání skončí výjimkou:
julia> q.i 0.33333334f0 julia> q.i=10 ERROR: type ImmutableQuat256 is immutable
Vidíme, že jediný okamžik, kdy lze zapisovat do položek neměnitelného typu, je konstrukce objektu tohoto typu (to ve skutečnosti není zcela přesné ve chvíli, kdy objekt obsahuje pole, ovšem tímto případem se prozatím nebudeme zabývat).
12. Parametrické typy
Typový systém jazyka Julia by nebyl úplný, pokud by v něm nebyly podporovány parametrické typy. Podívejme se nejdříve na příklad, z něhož bude patrné, jak lze parametrické typy použít a kde mají význam. Jedná se stále o deklaraci typu představujícího kvaternion, ovšem typy položek jsou parametrizovatelné:
julia> type PQuat{T} r::T i::T j::T k::T end
U takto definovaného kompozitního typu lze určit typ všech položek až při volání konstruktoru, a to například takto:
julia> x=PQuat{Float32}(1, 2, 3, 4) PQuat{Float32}(1.0f0,2.0f0,3.0f0,4.0f0) julia> y=PQuat{UInt8}(1, 2, 3, 4) PQuat{UInt8}(0x01,0x02,0x03,0x04)
či dokonce:
julia> z=PQuat{Bool}(1, 0, 1, 0) PQuat{Bool}(true,false,true,false)
Zde je nutné upozornit na to, že parametr je nedílnou součástí typu (tato informace není zapomenuta), o čemž se lze snadno přesvědčit:
julia> typeof(x) PQuat{Float32} julia> typeof(y) PQuat{Bool}
Parametry typu však mohou být i mnohem divočejší, například:
julia> type PPQuat{T1,T2,T3,T4} r::T1 i::T2 j::T3 k::T4 end julia> xx=PPQuat{Int,Any,Float32,Complex128}(42, "ahoj", 3.14, 1+2im) PPQuat{Int64,Any,Float32,Complex{Float64}}(42,"ahoj",3.14f0,1.0 + 2.0im) julia> typeof(xx) PPQuat{Int64,Any,Float32,Complex{Float64}}
Povšimněte si, že i Complex je ve skutečnosti parametrickým datovým typem!
13. Funkce a metody v programovacím jazyku Julia
Zajímavé je použití datových typů při tvorbě funkcí. V jazyce Julia může být funkce implementována formou několika metod (minimálně jedné metody), přičemž to, která konkrétní metoda se zavolá, se určí na základě typů předávaných parametrů. Zde je nutné zdůraznit, že tento princip je zobecněním běžných metod známých z OOP, protože tam se konkrétní metoda určuje na základě prvního parametru (this/self). Příklad nyní bude velmi jednoduchý – vytvoříme si funkci pro součet dvou hodnot, přičemž tato funkce bude implementována dvojicí metod. První metoda akceptuje dvě čísla (libovolného typu), druhá metoda akceptuje kvaterniony:
julia> function add(a::Number, b::Number) a+b end add (generic function with 1 method) julia> function add(a::Quat256, b::Quat256) Quat256(a.r+b.r, a.i+b.i, a.j+b.j, a.k+b.k) end add (generic function with 2 methods)
Nyní si můžeme tyto metody (neboli jedinou funkci add!) otestovat:
julia> add(x,y) Quat256(6.0f0,8.0f0,10.0f0,12.0f0) julia> add(1,2) 3 julia> add(1//2, 1//3) 5//6
Ovšem podle očekávání následující příkaz neprojde (nebyl by však problém ji dopsat):
julia> add(1//2, x) ERROR: MethodError: `add` has no method matching add(::Rational{Int64}, ::Quat256) Closest candidates are: add(::Number, ::Number) add(::Quat256, ::Quat256)
Typový systém společně s funkcemi/metodami představuje v programovacím jazyku Julia velmi silnou a přitom relativně snadno pochopitelnou techniku, kterou v případě potřeby dokážou využít i lidé, kteří nejsou profesionálními programátory, ale odborníky ve své oblasti (zpracování signálů, numerické výpočty, konstrukce atd.).
14. Odkazy na Internetu
- 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