Contents



Introduction

Julia can be used with dynamic and/or static typing. The Julia documentation is quite extensive but far from practical (IMHO). We have to remember that not all things that we construct dynamically can be compiled (e.g. using dynamically named dicts/hash tables). If we use as much proper type declaration, then we will reduce our errors and increase the speed of Julia quite a lot. Let’s have a brief look on how to do so.

Type system

The type system is organized hierarchically in a tree structure. The root node is the Any type:

subtypes(Any)
394-element Array{Any,1}:
 AbstractArray                   
 
 WeakRef
 
print(subtypes(Any))
Any[AbstractArray, AbstractChannel, AbstractChar, AbstractDict, AbstractDisplay, AbstractSet, AbstractString, Any, AbstractCartesianIndex, AbstractCmd, ArithmeticStyle, AsyncCollector, AsyncCollectorState, AsyncCondition, AsyncGenerator, AsyncGeneratorState, Keyword, BitMaskedBitArray, BroadcastStyle, Broadcasted, Extruded, LReplace, CodegenParams, AbstractLogger, LogLevel, LogState, CyclePadding, DataTypeLayout, DimSelector, Binding, DocStr, MultiDoc, EachLine, EachStringIndex, FileRedirect, StatStruct, uv_dirent_t, GC_Diff, GC_Num, Generator, GitVersionInfo, Bignum, Float, IOServer, InterpreterIP, IteratorEltype, IteratorSize, AbstractZipIterator, Count, Cycle, Drop, Enumerate, Filter, Flatten, IterationCutShort, PartitionIterator, ProductIterator, Repeated, Rest, Reverse, Stateful, Take, JLOptions, FILE, TimeVal, TmStruct, DoubleFloat32, DoubleFloat64, MethodList, MultiplicativeInverse, Ordering, OrderStyle, Padding, PaddingError, PkgId, Prehashed, RangeStepStyle, RegexMatchIterator, ReshapedArrayIterator, ReshapedIndex, SHA1, Semaphore, SkipMissing, Algorithm, StackFrame, SummarySize, CPUinfo, UV_cpu_info_t, AbstractLock, Atomic, TwicePrecision, UUID, GraphemeIterator, ValueIterator, Buffer, Condition, Box, CodeInfo, AbstractDict, AbstractSet, Argument, ArithmeticStyle, BBIdxIter, BasicBlock, BlockLiveness, CFG, CFGInliningState, CodegenParams, Conditional, Const, ConstantCase, DataTypeLayout, DelayedTyp, DomTree, DomTreeNode, DominatedBlocks, DynamicCase, ExponentialBackOff, Generator, GotoIfNot, IRCode, IncrementalCompact, IndexStyle, InferenceResult, InferenceState, InliningTodo, InterpreterIP, InvokeData, IteratorEltype, IteratorSize, AbstractZipIterator, Count, Cycle, Drop, Enumerate, Filter, Flatten, IterationCutShort, PartitionIterator, ProductIterator, Repeated, Rest, Reverse, Stateful, Take, JLOptions, LiftedPhi, MaybeUndef, MethodList, Missing, NewNode, NewSSAValue, NotFound, OOBToken, OldSSAValue, OptimizationState, Ordering, OrderStyle, Pair, Params, PartialTypeVar, RangeStepStyle, ReturnNode, SSADefUse, SimpleCartesian, SlotInfo, Some, Algorithm, StateUpdate, TypesView, UndefToken, UnionSplit, UnionSplitSignature, UseRef, UseRefIterator, Val, ValueIterator, VarState, GeneratedFunctionStub, GotoNode, LineInfoNode, MethodInstance, MethodTable, NewvarNode, PhiCNode, PhiNode, PiNode, SSAValue, SimpleVector, Slot, TypeMapEntry, TypeMapLevel, TypeName, UpsilonNode, Cstring, Cwstring, AbstractDateToken, AbstractTime, DateFormat, DateFunction, DateLocale, DayOfWeekToken, Decimal3, TimeZone, DLMHandler, AbstractMsg, AbstractRemoteRef, AbstractWorkerPool, ClusterManager, LocalProcess, MsgHeader, ProcessGroup, RRID, RemoteValue, Worker, WorkerConfig, Enum, Exception, ExponentialBackOff, Expr, FDEvent, FDWatcher, FileEvent, FileMonitor, FolderMonitor, PollingFileWatcher, _FDWatcher, Function, GlobalRef, HTML, IO, IndexStyle, AbstractCredential, AbstractGitHash, AbstractGitObject, BlameHunk, BlameOptions, Buffer, CachedCredentials, CheckoutOptions, CherrypickOptions, CloneOptions, CloneOptionsStruct, ConfigEntry, CredentialPayload, DescribeFormatOptions, DescribeOptions, DiffDelta, DiffFile, DiffOptionsStruct, ErrorStruct, FetchHead, FetchOptions, FetchOptionsStruct, GitCredential, GitCredentialHelper, IndexEntry, IndexTime, MergeOptions, ProxyOptions, PushOptions, PushOptionsStruct, RebaseOperation, RebaseOptions, RemoteCallbacks, RemoteCallbacksStruct, Signature, SignatureStruct, State, StatusEntry, StatusOptions, StrArrayStruct, TimeStruct, TransferProgress, dl_phdr_info, LineNumberNode, AbstractRotation, Factorization, UniformScaling, MIME, Admonition, BlockQuote, Bold, Code, Config, Footnote, Header, HorizontalRule, Image, Italic, LaTeX, LineBreak, Link, List, MD, Paragraph, Table, Method, Missing, Module, NamedTuple, Nothing, Number, Pair, DiffEntry, InProject, VerInfo, MiniProgressBar, Graph, GraphData, ResolveLog, ResolveLogEntry, VersionInterval, VersionSet, Line, ArgSpec, CommandSpec, CompletionCache, Option, OptionSpec, PkgCommand, QuotedWord, Rev, Statement, FieldValue, MaxSumParams, Messages, NodePerm, SolutionTrace, VersionWeight, Parser, Table, Context, EnvCache, Fixed, GitRepo, PackageSpec, VersionBound, VersionRange, VersionSpec, ProfileFormat, StackFrameTree, QuoteNode, AbstractREPL, CompletionProvider, HistoryProvider, InputAreaState, KeyAlias, MIState, ModeState, TextInterface, Options, REPLBackend, REPLBackendRef, Completion, AbstractMenu, AbstractRNG, DSFMT_state, GF2X, FloatInterval, Sampler, UniformBits, RawFD, ReentrantLock, Ref, Regex, RegexMatch, RoundingMode, HMAC_CTX, SHA_CTX, AbstractSerializer, IPAddr, InetAddr, Some, CapturedScalars, SuiteSparseStruct, Symbol, Task, AbstractTestSet, ExecutionResult, Ignored, LogRecord, Result, Text, Timer, Tuple, Type, TypeVar, UndefInitializer, Val, Vararg, VecElement, VersionNumber, WeakRef]

Since numbers are most important for us, it is nice to have some visualization (using graphviz):

I got some errors trying to install all required packages from the JuliaGraphs environment. Hence, I did this detour via graphviz.

function getSubtypes(Input)
    for Subtype in subtypes(Input)
        push!(TypeStructure, [string(supertype(Subtype))*" -> "*string(Subtype)*";"]);
        getSubtypes(Subtype)
    end
 end
TypeStructure = []
getSubtypes(Number)


GraphName = "JuliaNumberTypes"
Preamble = "digraph "*GraphName*"{"
EndOfFile = "}"

open("./"*GraphName*".dot", "w") do f
    write(f, Preamble)
    for i in TypeStructure
        write(f,i[1]*"\n")
    end
    write(f,EndOfFile)
end

convert it from dot to SVG

dot -Tsvg JuliaNumberTypes.dot -o JuliaNumberTypes.svg


JuliaNumberTypes



Number

Number



Complex

Complex



Number->Complex





Real

Real



Number->Real





AbstractFloat

AbstractFloat



Real->AbstractFloat





AbstractIrrational

AbstractIrrational



Real->AbstractIrrational





Integer

Integer



Real->Integer





Rational

Rational



Real->Rational





BigFloat

BigFloat



AbstractFloat->BigFloat





Float16

Float16



AbstractFloat->Float16





Float32

Float32



AbstractFloat->Float32





Float64

Float64



AbstractFloat->Float64





Irrational

Irrational



AbstractIrrational->Irrational





Bool

Bool



Integer->Bool





Signed

Signed



Integer->Signed





Unsigned

Unsigned



Integer->Unsigned





BigInt

BigInt



Signed->BigInt





Int128

Int128



Signed->Int128





Int16

Int16



Signed->Int16





Int32

Int32



Signed->Int32





Int64

Int64



Signed->Int64





Int8

Int8



Signed->Int8





UInt128

UInt128



Unsigned->UInt128





UInt16

UInt16



Unsigned->UInt16





UInt32

UInt32



Unsigned->UInt32





UInt64

UInt64



Unsigned->UInt64





UInt8

UInt8



Unsigned->UInt8






Julia uses default Float and Int length depending on the architecture it is running on (e.g. Float64 and Int64 on standard Linux x86_64 installations). Technically, we have some more Complex types:

supertype(ComplexF16)
supertype(ComplexF32)
supertype(ComplexF64)

They are “dummy” childs of Complex and therefore are not shown in the graph above.

Here is the full list of all Types of the standard library/environment while running inside a Jupyter notebook:

TypeStructure = []
getSubtypes(Any)

If we visualize it the way we did above, then we will end up with:

What have I done…. ;)

dot -Tsvg JuliaTypes.dot -o JuliaTypes.svg
Error: JuliaTypes.dot: syntax error in line 6 near '.'

less JuliaTypes.dot
digraph JuliaTypes{Any -> AbstractArray;
AbstractArray{T,1} where T -> AbstractRange;
AbstractRange -> LinRange;
AbstractRange -> OrdinalRange;
OrdinalRange{T,T} where T -> AbstractUnitRange;
AbstractUnitRange{T} where T<:Integer -> Base.OneTo;
...

JuliaTypes.dot lines 1-38/1980655 0%

Let’s rewrite it a bit:

function getSubtypes(Input)
    for Subtype in subtypes(Input)
        # replacing characters in a string is a tiny bit more complicated
        AboveType = replace(replace(string(supertype(Subtype)), r"{|}|,|>|<|;|:| " => "_"),  "." => "_");
        CurrentType = replace(replace(string(Subtype), r"{|}|,|>|<|;|:| " => "_"),  "." => "_");
        push!(TypeStructure, [AboveType*" -> "*CurrentType*";"]);
        getSubtypes(Subtype)
    end
 end
TypeStructure = []
getSubtypes(Any);
GraphName = "JuliaTypes"
Preamble = "digraph "*GraphName*"{"
EndOfFile = "}"
open("./"*GraphName*".dot", "w") do f
    write(f, Preamble)
    for i in TypeStructure
        write(f,i[1]*"\n")
    end
    write(f,EndOfFile)
end
dot -Tsvg JuliaTypes.dot -o JuliaTypes.svg
Error: Edge length 391416.489043 larger than maximum 65535 allowed.
Check for overwide node(s)

JuliaTypes.svg ends up to be > 900 MB. I haven’t found any useful way to display this here. I’m also not commenting on how much RAM it will require if opened with a browser or Inkscape.

Examples

Variables

x::Int16 = 100
ERROR: syntax: type declarations on global variables are not yet supported
X = 100::Int16
ERROR: TypeError: in typeassert, expected Int16, got Int64
Stacktrace:
 [1] top-level scope at none:0
X = 100::Int64
100

Hence, we have to remember that local assignment works only inside of functions.

We can create structs as well to enforce certain types.

Array

We can define types and dimensions of arrays. They can speed up certain computations quite a lot. There are also static arrays types available.

# A = Array{Type,Dimension}[]
A = Array{UInt8,2}[]
0-element Array{Array{UInt8,2},1}

UInt8 is stored/displayed as hex!!

With this kind of array we can add this 2D array [0x02 0x03] to it but not this 1D array [0x02;0x03].

push!(A,[0x02 0x03])
1-element Array{Array{UInt8,2},1}:
 [0x02 0x03]
 
push!(A,[0x02; 0x03])
MethodError: no method matching Array{UInt8,2}(::Array{UInt8,1})

Dict

The “supertype” of all dicts is the AbstractDict which is a direct subtype of Any:

supertype(AbstractDict)
Any


subtypes(AbstractDict)
7-element Array{Any,1}:
 Base.EnvDict        
 Base.ImmutableDict  
 Base.Iterators.Pairs
 Dict                
 IdDict              
 Test.GenericDict    
 WeakKeyDict

For most purposes, we are probably using Dict.

aDict = Dict()
Dict{Any,Any} with 0 entries

aDict = Dict{String,Array{UInt8,1}}()
Dict{String,Array{UInt8,1}} with 0 entries
aDict["Entry1"] = [0x01; 0x02]
2-element Array{UInt8,1}:
 0x01
 0x02
aDict["1"] = [0xf3]
1-element Array{UInt8,1}:
 0xf3
aDict["a82"] = [0x01; 0xff; 0x84]

3-element Array{UInt8,1}:
 0x01
 0xff
 0x84

If we use the wrong types:

aDict[1] = [0xef]
MethodError: Cannot `convert` an object of type Int64 to an object of type String
aDict["failure"] = ["a String"]
MethodError: Cannot `convert` an object of type String to an object of type UInt8
aDict
Dict{String,Array{UInt8,1}} with 3 entries:
  "1"      => UInt8[0xf3]
  "Entry1" => UInt8[0x01, 0x02]
  "a82"    => UInt8[0x01, 0xff, 0x84]

Functions

We can define the input types of functions to make sure that we detect errors right at the beginning of calling a function and therefore we can save time to look for such errors inside a function (especially if the output could be Float as well but we need Int).

function Fibonacci(n::Int64,)
    FibonacciSequence = Array{Int64,1}([0;1])
    for i in collect(2:n)
        push!(FibonacciSequence, FibonacciSequence[end] + FibonacciSequence[end-1])
    end
    return FibonacciSequence
end    

a = Fibonacci(9.9)
MethodError: no method matching Fibonacci(::Float64)
Closest candidates are:

a = Fibonacci(5)
6-element Array{Int64,1}:
 0
 1
 1
 2
 3
 5