Types
Ringkasan Pelajaran
# Introduction
About
So far, the syllabus has not said much about types, but clearly they exist in Julia:
julia> vals = (42, 4.3, π, "hello", 'Q')
(42, 4.3, π, "hello", 'Q')
julia> typeof(vals)
Tuple{Int64, Float64, Irrational{:π}, String, Char}
We never specified the types, but Julia assigned them regardless.
- Julia has
types, which are central to its design. - Julia can usually “guess” the type, using
Type Inference.
The JIT compiler will look through (all) the code, see how a variable is used, and infer a suitable default type compatible with that usage.
Type promotion
There have been many examples in previous concepts which coerce numerical types into uniformity, starting with the simplest arithmetic.
julia> 2 + 1.3
3.3
We added an integer (Int64) and a floating-point (Float64) number and got a Float64 result. Similarly:
julia> nums = (3, 4.1, 1//4)
(3, 4.1, 1//4)
julia> typeof(nums)
Tuple{Int64, Float64, Rational{Int64}}
julia> [nums...]
3-element Vector{Float64}:
3.0
4.1
0.25
In the case above, a tuple preserved the type of each element, but converting to a vector changed all of them to a uniform Float64.
The Julia compiler understands which type conversions are possible: integer to float is no problem, float to integer loses precision so an InexactError is raised.
Type promotion converts all the values in the expression to a common type, versatile enough to be compatible with all the inputs.
The same conversion can be done explicitly with the promote() function:
julia> promote(nums...)
(3.0, 4.1, 0.25)
Type assignment
Relying on type inference and type promotion is fine for solving simple tutorial exercises, but for bigger programs you are likely to need more precise control.
On most modern processors, an integer defaults to Int64.
We saw in the Numbers Concept that a value can be converted to a particular non-default type.
julia> x = Int16(42)
42
julia> typeof(x)
Int16
However, the variable x can still be reassigned to a different type:
julia> x = "changed"
"changed"
julia> typeof(x)
String
This is type instability, which is:
- Very convenient in small scripts.
- Bad for performance and reliability in bigger programs.
Instead, we can set the type of x by using the :: operator:
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> y = "changed"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int16
The function `convert` exists, but no method is defined for this combination of argument types.
Now y is, and always will be, of type Int16.
Thus, the compiler knows how many bytes to reserve for it, and can optimize the rest of the code to rely on a stable type.
In this, the variable is more or less similar to those in a statically-typed language such as C.
Type Assertion
Type assignment, described above, is used on the left-hand side of a variable assignment to constrain the type of that variable.
Using the :: operator with a value, or something that evaluates to a value, is generally an assertion that the value must be of this type, otherwise an error should be thrown.
julia> 42::Number
42
julia> "two"::Number
ERROR: TypeError: in typeassert, expected Number, got a value of type String
Commonly, this might be used with the return value of a function, as a simple last check that the function behaved as expected.
Note that there is an @assert macro for other forms of assertion.
The Type Hierarchy
Int64, Int16, String, Char: where do these types “come from”.
In many object-oriented (OO) languages, each type is a class, subclassing arranges them in a class hierarchy, and class methods define the behaviors.
Java and Ruby are obvious examples of this pattern, but even Python is similar internally.
Julia has no classes.
The documented reason is that OO features interfere with the JIT compiler and hurt runtime performance.
Who knows, perhaps they also read this quote:
“Object-oriented programming is an exceptionally bad idea which could only have originated in California.”
It is attributed to Edsger Dijkstra, a brilliant computer scientist for several decades from the 1950’s (though not generally noted for his sunny optimism or subtle diplomacy).
And yet, look at this code:
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> supertype(Int16)
Signed
julia> supertypes(Int16)
(Int16, Signed, Integer, Real, Number, Any)
Filling in some details:
Int16is a type, and we can create variables of this type.Int16is a subtype ofSigned, and thesupertype()function shows us this.- There is a hierarchy of types, going up through
Integer,RealandNumbertoAnyat the top, andsubtypes()will list this branch of the hierarchy for us.
All branches end in Any, which is unique in being its own supertype.
julia> supertypes(String)
(String, AbstractString, Any)
julia> supertype(Any)
Any
So, Julia has no class hierarchy, but it does have a type hierarchy.
Trying to show the entire hierarchy gives a huge tree, impossible to really view.
Looking at parts of it is something discussed online.
Eventually, we will try to unpick how this works, but there is much more to explore first.
Testing for Types
We can use typeof() to test for equality in the usual way.
julia> typeof(11)
Int64
julia> typeof(11) == Int64
true
julia> typeof(11) == Number
false
The type equality must be exact, as this form of comparison has no understanding of the type hierarchy.
More flexibly, isa will tell us if a value has either the same type as a comparator, or a subtype of it.
It can be used in either infix or function form.
julia> 12 isa Int64
true
julia> 12 isa Number
true
julia> isa(12, Number)
true
julia> 12 isa String
false
Note that isa expects a value on the left, not a type.
Trying to compare two types this way will give unexpected results.
The correct operator is <:, which we will see a lot more of in future concepts.
julia> Int64 isa Number ## Don't do this!
false
julia> Int64 <: Number
true
Testing for types can be used to control flow within a function, but this is relatively unusual in idiomatic Julia.
We will see in the Multiple Dispatch Concept that it is often more efficient to add types to function arguments and let Julia’s dispatch mechanism handle such logic. However, there are multiple further type-related concepts we need to discuss before we reach Multiple Dispatch.
Abstract versus Concrete Types
We saw that the type hierarchy forms a tree structure (in the Computer Science sense, with the root at the top).
Each item in the tree is a node, and these can be divided into categories:
julia> subtypes(Integer) # an abstract type
3-element Vector{Any}:
Bool
Signed
Unsigned
julia> subtypes(Int64) # a concrete type
Type[]
This is an important distinction, because only concrete types can be instantiated as variables.
julia> a::Int16 = 42
42
julia> typeof(a)
Int16
julia> b::Integer = 42
42
julia> typeof(b)
Int64
Note that trying to use an abstract type gives no error message (in this case), but the compiler creates an appropriate concrete type: Int64 instead of Integer.
At least it prevents the variable being assigned to a non-integer:
julia> f::Integer = "hello"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Integer
The function `convert` exists, but no method is defined for this combination of argument types.
Type assignment with an abstract type is thus a constraint on the variable type, to any subtype of (in the above case) Integer.
This is something unusual in the world of programming languages: weaker than a type assignment in C, stronger than a type hint in recent versions of Python.
The functions isabstracttype() and isconcretetype() allow testing.
Note that these are not just negations of one another: we will see in a later Concept that some types can be neither abstract nor concrete.
julia> isconcretetype(Integer), isabstracttype(Integer)
(false, true)
julia> isconcretetype(Int64), isabstracttype(Int64)
(true, false)
# Vector is neither
julia> isconcretetype(Vector), isabstracttype(Vector)
(false, false)
Though Vector is not a concrete types, the elements are concrete.
The eltype() (element type) function extracts this type:
julia> eltype([1, 2.3])
Float64
Originally from Exercism julia concepts