Skip to content

Variables and Types

When you issue an assignment statement, such as

x = 2

you create a variable called x. What does this actually mean?

Julia creates a place in memory where the value 2 is stored. It then creates a "binding" so that the symbol :x from now on points to that location in memory. (This is actually not the way it works for scalar real numbers, but we will set this detail aside right now.)

The memory location now has a type. In this case, it is Int64:

typeof(x)
    Int64

Julia is dynamically typed. This means that I can change the type of x by simply reassigning a new value or by converting the value to a different type:

x=Float64(x)
    2.0

Doing this kind of thing is generally a bad idea, but it is legal. It is a bad idea because it confuses the reader of the code and the compiler.

Important types include:

  1. Scalar numbers, such as 1 or 1.0.
  2. Strings, such as "this is a string".
  3. Arrays of numbers, such as [1 2; 3 4].
  4. Dict: a container that maps names to values; similar to a struct in Matlab.
  5. Struct: a composite, user-defined type with fixed fields; similar to a class in Matlab.

Supertypes and Subtypes

One DataType is Number. This includes integers, floating point numbers, etc.

Types come in hierarchies. For example:

Base.show_supertypes(Int64)
    Int64 <: Signed <: Integer <: Real <: Number <: Any

shows that Int64 is a subtype of Signed etc.

Only the "lowest" types in the hierarchy are concrete types that can actually be assigned values. The higher up types are abstract. They only exist to make a type hierarchy.

Why do types come in a hierarchy? Mainly because some functions are defined on certain groups of types, but not on others. For example, sin("abc") does not make sense, but sin(x) makes sense for various types of Number.

Scalar Numbers

The main Number types that we care about are integers and floats. Integers come in various flavors depending on how many digits they can store and whether or not they are signed. The default is Int64 which stores 64 bits and is signed. UInt8 requires only 8 bits, but it cannot store large values.

Julia knows standard operators on numbers and is smart enough to convert types as needed:

julia> x= 1 + 2.0
3.0

julia> typeof(x)
Float64

Overflow warning: If you assign a value that is too large for a type to store, you get overflow issues:

y=UInt8(12345678)
    InexactError: trunc(UInt8, 12345678)

    Stacktrace:

     [1] throw_inexacterror(::Symbol, ::Type{UInt8}, ::Int64) at ./boot.jl:558

     [2] checked_trunc_uint at ./boot.jl:588 [inlined]

     [3] toUInt8 at ./boot.jl:650 [inlined]

     [4] UInt8(::Int64) at ./boot.jl:710

     [5] top-level scope at In[5]:1

     [6] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091

This throws an error because Julia cannot convert a large number to a UInt8.

The several lines below the error message are a stacktrace. It shows where in your code the error occurred. This is important for debugging.

We will talk about arrays separately.

Strings

In contrast to Matlab, Strings are not Vector{Char}. They are their own type.

A String is created by providing characters inside quotes:

a = "This is a string"

But strings can be indexed like character vectors:

julia> a[4]
's': ASCII/Unicode U+0073 (category Ll: Letter, lowercase)