Go Inspired Tour of Julia

A quick tour of the basic features of the Julia programming language.

Erik Engheim
7 min readOct 15, 2017
Running some Julia code in the Julia REPL (Read Evalulate Program Loop) enironment.

I have fond memories learning Google’s programming language Go. It is the language I’ve probably been able to pick up most quickly and do something useful with. The documentation always seemed to strike a very sensible balance between giving you enough information and not going overboard with theory.

One of my favorite first experiences was a web page with a tour of the language. Every page showed one tiny code example in Go, demonstrating one key feature. You could modify the code and run it in the browser. It was a great way to get a quick introduction.

Inspired by this I am recreating the Go tour for the Julia programming language. Sadly I cannot offer you an executable environment here. But you can find the source code here.

Read more: Why I Wrote a Julia Programming Book.

Hello World

Writing to the screen (standard output).

println("Hello, 世界")

Functions

Functions can take zero or more arguments. In this example, add takes two parameters of type Int (64-bit in this case).

As with Go, the type comes after the name of the parameters. Unlike Go however Julia is not statically typed, and doesn’t require specification of type.

Note return is optional in this case, as Julia functions always returns the last evaluated expression, and everything is an expression in Julia.

function add(x::Int, y::Int)
return x + y
end

println(add(42, 13))

Variables

Just assign to variables, no need for a special keyword like var to indicate you are creating variables.

x, y, z = 1, 2, 3
julia, python, java = true, false, "no!"

println(join([x, y, z, julia, python, java], ' '))

Outputs:

1 2 3 true false no!

However Julia has the keywords local and global to indicate scope, where you need to be explicit about it.

Here is an example illustrating their usage.

x = 10
function foo()
x = 4

global y
y = 5
return x
end

Say we use this function in the Julia REPL, we’ll get these results:

julia> foo()
4

You can see it returns 4, as expected. But if you check the value of x it is still 10, because only a local x was changed.

julia> x
10

However y actually got changed because it was not a local y. We explicitly said it was global.

julia> y
5

For Loop

For-loop is the most normal looping construct in Julia. For can iterate over collections or ranges such as 0:9.

sum = 0
for i in 0:9
sum += i
end
println(sum)

Outputs: 45

You can even specify ranges with step values such as 0:2:9, for every other value or negative ranges, to count backwards such as 10:-1:0.

While Loop

It is possible to use a while loop as well even though it is less versatile.

sum = 1
while sum < 1000
sum += sum
end
println(sum)

Outputs: 1024

If Statement

As with Go, we don’t use parenthesis for control structures such as for, while and if. Unlike Go there is no need to use curly brackets to indicate code blocks.

function mysqrt(x::Float64)
if x < 0
string(mysqrt(-x), "i")
else
string(sqrt(x))
end
end

println(mysqrt(2.0), " ", mysqrt(-4.0))

Outputs: 1.4142135623730951 2.0i

Structs

Julia is similar to Go in that there is no implementation inheritance and it uses simple structs to define composite types.

struct FooBar
spam::Char
foo::Int64
bar::String
end

You don’t have to specify type information but, when you do that makes it possible to lay out instances of FooBar in memory the same way as in C or Go.

a = FooBar('B', 12, "Days")

The fields can be accessed in various ways. Normal way:

julia> a.spam
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)

By providing a symbol object, equal to the name of the field:

julia> getfield(a, :foo)
12

Giving an index to the field:

julia> getfield(a, 3)
"Days"

Dictionary

A dictionary maps keys to values.

struct Vertex
lat::Float64
long::Float64
end

m = Dict{String, Vertex}()
m["Bell Labs"] = Vertex(40.68433, -74.39967)
println(m["Bell Labs"])

We can initialize the dictionary directly with its keys and values as well.

m = Dict("Bell Labs" => Vertex(40.68433, -74.39967))

Arrays

Unlike Go, but like most languages the length is not part of normal Julia arrays. Vector{Int} is one dimensional array of integers. Below Julia deduces the type of the array elements:

p = [2, 3, 5, 7, 11, 13]
println("p == ", p)
for i = 1:length(p)
println("p[$i] == $(p[i])")
end

You can specify that elements should be of specific types, however. This makes an array, where each element is an 8-bit unsigned integer.

p = UInt8[2, 3, 5, 7, 11, 13]

Julia being aimed at numerical computing, has been built with multidimensional arrays from the start, so you can create e.g. matrices easily.

julia> m = [1 2 3; 3 4 5]
2×3 Array{Int64,2}:
1 2 3
3 4 5

Slices

You can easily pull out sub sections of an array, called slices in Julia, just like in Go.

a = [2, 4, 6, 8, 10]
println(a)
println(a[1:2])
println(a[1:4])
println(a[3:3])

However it is a more generic thing in Julia a not built into the language. Rather it relies on the fact that you can pass any object to the index operator in Julia, and it will simply invoke the appropriate getindex() function.

  • a[1] will invoke the getindex(A::Array, i::Int) function.
  • a[2:3] will invoke getindex(A::Array, I::UnitRange{Int}), because 2:3 creates a unit range object.

This extents to anything so you could also pass other arrays as an argument to the index operator:

julia> a[[3,2]]
2-element Array{Int64,1}:
6
4

Which causes Julia to treat each element in the array as an index into the a array.

Function Values

Functions are values too. They can be passed around just like other values.

Function values may be used as function arguments and return values.

hypot = (x, y) -> sqrt(x^2 + y^2)
println(hypot(3, 4))

Function Closures

Julia functions may be closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is “bound” to the variables.

For example, the adder function returns a closure. Each closure is bound to its own sum variable.

function adder()
sum = 0
x -> sum += x
end

pos, neg = adder(), adder()
for i = 0:9
println(pos(i), " ", neg(-2*i))
end

Compared to the Go example you can see that we don’t have to explicitly return values, when defining functions and closures. Go needs to because it distinguishes between expressions and statements.

Methods

Neither Julia nor Go is really object oriented languages, however Go does have method in the sense that you can associate functions with particular types.

Julia however does not have methods in the traditional OOP sense. Methods in OOP means you pick a particular function implementation based on the first argument to a function, the this pointer. Normally this argument is not explicitly stated in OOP languages except in Go and Python.

Julia however will pick which method to execute at runtime based on the type of all the arguments of the function. So in the example below, we got one function abs() with two methods:

  • One operating on verticies
  • The other on floating point numbers.
struct Vertex
x::Float64
y::Float64
end

function abs(v::Vertex)
sqrt(v.x^2 + v.y^2)
end

function abs(f::Float64)
f < 0 ? -f : f
end

v = Vertex(3, 4)
println(abs(v))

v = -sqrt(2)
println(abs(v))

Enumerations

Go comes with quite a lot of functionality built directly into the language, such as dictionaries and arrays. Julia is quite the opposite, dictionaries and arrays are supplied as library functionality. Even numbers which are normally built into a language, are defined by libraries in Julia. So you can in principle define your own number types.

This goes for things like enums as well. They are not built into the language, but in the Julia standard library we have macros which allow you to create enums. All macros start with a @.

@enum Weekday monday tuesday wednesday thursday friday

function describe(day::Weekday)
if day == monday
"Moon day"
elseif day == tuesday
"Tyr's day"
else
string(day)
end
end

println(describe(monday))
println(describe(tuesday))
println(describe(wednesday))

What the @enum macro does is really just defining a type Weekday in this case with concrete instances named monday, tuesday etc. It also defined functions so that string() will return a sensible string for a given enum.

Where to Go From Here

I’ve only scratched the surface of Julia in this tour. It is not really that the language is very complicated, but it has a very powerful core, which allows you to create a lot of powerful constructs.

If you want to learn more, you can head over to the official Julia website. Or check out some of my videos:

--

--

Erik Engheim

Geek dad, living in Oslo, Norway with passion for UX, Julia programming, science, teaching, reading and writing.