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
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