Dive Deep Into Julia Types

Adarsh Ms
The Startup
Published in
5 min readSep 3, 2020

--

Get to know about some built-in operators, literals and Unions

This is part 5 of the Julia Tutorials (a series of tutorials on Julia). If you are not familiar with Julia Types, please go through part 4 (Variables and Types in Julia) for better understanding.

If you are not familiar with Julia and want to get started with it, feel free to check out the complete series @ —

Note: Use Julia’s REPL to quickly follow along with the codes in this tutorial

Okay, as discussed in the previous tutorial let’s get started by demystifying the :: operator. Before going there, let’s get acquainted with a built-in function typeof() , which can be used to obtain type of a value. You can see it’s usage in the below example.

:: has different functionalities in different contexts, let’s see what they are:

Declaring a (sub)type

In the context of a statement, such as a function, :: can be used to restrict the type of a variable.

Example:

# Declare the function
function restrict_this_integer()
x::Int8 = 32
x
end

# Call the function
p = restrict_this_integer()

typeof(p) # Output will be Int8

Note: Don’t worry about the function definition, it will be discussed in a follow up tutorial.

Asserting a type

In every other context, :: means 'I assert this value is of this particular type'. This is a great way to check a value for both abstract and concrete type.

Example:

integer_value = 10# Assert integer_value if of type Int64
integer_value::Int64
# Assert integer_value if of type Char
integer_value::Char

The assertion for Int64 will return the value stored in “integer_value” and the assertion for Char will result in a TypeError — ERROR: TypeError: in typeassert, expected Char, got Int64 .

Specifying acceptable function inputs

While we haven’t really discussed function inputs, you should be familiar with the general idea of ​​a function — mapping a tuple of arguments using some expression to a return value. Julia can ensure that a function only accepts values ​​that we want.

Let’s define a function which takes 2 integers as input and provides their sum as output.

function add(x::Int64, y::Int64)
x + y
end

# Call the function on 2 integers
result = add(16, 14)
Outputs -> 30
# Call the function on 2 floats
result = add(2.0, 3.5)
Outputs -> ERROR: MethodError: no method matching add(::Float64, ::Float64)

As you can see from the above examples, any types other than the specified one ( Int64 ) results in an error.

Literals

Literals are means of expressing specific values in a program.

Strings
We’ve already came across the string literals in some examples, String literals can be created by enclosing the value between " (double quotes).

string_val = "test string"

Note: In Julia, strings can only be defined between " (double quote), while '(single quote) is used for defining a character.

char_val = 'a'

Arrays/Lists
Array literals are N-dimensional mutable containers.

  • Each element they contain has to be either a variable or otherwise a valid literal.
  • Can be heterogeneous, i.e, values don’t have to be of same type.
  • Values are separated by a , (comma)
  • Values can be updated by subscripting through their index.

Note: Compared to other programming languages, Julia uses 1 indexing rather than 0 indexing, i.e, indexes start at 1 instead of 0.

1-D arrays — A linear array which has only one subscript,

# Initialize an array with values
arr1 = [1, 2, "sausage", 5.9]
# Initialize an empty array
arr2 = []
# Update value by index
arr1[1] = 3 # Updates 1st element of arr1

Multidimensional arrays — A rectangular array of numbers or symbols arranged in rows and columns, also known as matrix. A matrix can be declared in 2 ways,

mat1 = [[1,2,3] [4,5,6] [7,8,9]]--or--mat2 = [1 2 3; 4 5 6; 7 8 9]# Update 2nd column in 1st row
mat1[1][2] = 5

While creating a multidimensional array, each row has to be of the same length, otherwise, it raises an error.

Elements of bidimensional arrays can be accessed with the array[row,col] or array[row][col] syntax.

Nested arrays — Simply speaking, these are an array of arrays, i.e, a 1-D array with vector values.

arr2 = [[1,2,3], [4,5,6], [7,8,9]]

This creates a 1-D array with 3 elements (each of which are vectors). Note the , separation between each vector elements.

Tuples
Tuples are similar to 1-D arrays, but unlike them, tuples are immutable.

tuple1 = (1, 4, 5.5, "test")

NamedTuples
NamedTuples are collections of items whose position in the collection (index) can be identified not only by the position but also by name.

namedtuple1 = (a=1, b=2, c=3)# Accessing elements by name
namdetuple1.a
namedtuple1.c

Dictionaries
Dictionaries are used to store key-value pairs.

# Initialize a dictionary with values
dict1 = Dict('a'=>1, 'b'=>2, 'c'=>3)
# Initialize an empty dictionary
dict2 = Dict()
# Add new pairs to dictionary
dict1['d'] = 4
dict2['e'] = 5
# Update existing pairs
dict['a'] = 8

They may seem similar to NamedTuples but both have some major differences,

  • Dictionaries are mutable while NamedTuples are immutable
  • Dictionaries are type unstable if different type of values are stored, while NamedTuples remain type-stable
d = Dict(k1=>"v1", k2=>2)  # Dict{Symbol,Any}
nt = (k1="v1", k2=2,) # NamedTuple{(:k1, :k2),Tuple{String,Int64}}

Sets
Sets are unordered collections used to store unique values.

# Create an empty set
set1 = Set([])
# Create a set with values
set2 = Set([1, 2, 3])

Though duplicate entries are present at the time of set construction, the resulting set will only have unique values

set3 = Set(["abcd", "efg", "efg", "hijk"])Output -> Set(["hijk", "efg", "abcd"])

Ranges
A range in Julia is simply a shorthand for a sequence of numbers that are all spaced equally.

Syntax:

range(start:[step]:end)
  • start — denotes the start value of the range
  • step — is optional, specifies the spacing between each values in the sequence
  • end — denotes the end value of the range

Ranges are lazy, meaning the values are not immediately available until they are needed. However, the generation of the values can be forced by using the collect() function.

0:20    # This return UnitRange{Int64}collect(0:2:20)    # This will generate a 6-element Array{Int64,1}collect(0.5:5.5)   # This will generate a 6-element Array{Float64,1}

Type Unions

Sometimes, it’s useful to have a single alias for multiple types. To do so, we can create a type union using the constructor Union:

Numeric = Union{Int, Float64}# Assert for Int
5::Numeric # Returns 5
# Assert for Float
7.06::Numeric # Returns 7.06
# Assert for string
"abcd"::Numeric # Results in TypeError

As you can see from the above example, since String type is not in the Uninon Numeric , asserting for a string will raise TypeError.

--

--

Adarsh Ms
The Startup

A passionate engineer who thrives on using programming languages to converse with machines.