Elixir Beginners Tutorial

This tutorial was originally written for the Women Who Code Sydney workshop https://www.meetup.com/Women-Who-Code-Sydney/events/254987164/

Skill level for this tutorial:

This tutorial is aimed at people who are new to coding

If you have coded before, you may want to try this tutorial: Elixir Beginner II Tutorial” from this page https://tinyurl.com/wwc-elixir

What is Elixir?

Elixir is a functional programming language which started as an R&D project of Plataformatec and now used by companies such as The Bleacher Report, Pinterest, Moz, Expert360, Culture Amp and many others! (The syntax can appear Ruby-like at first and beginners may appreciate some of the similarities here)

What we’ll be covering

In this tutorial we will be covering a few topics:

• Data Types (String, Numeric, Boolean, Atom)
• Lists
• Maps
• Pattern Matching
• Conditionals (if, case, cond)
• Functions
• Pipe Operator

Elixir is a language with many features, but hopefully you’ll be able to get through the above in a couple of hours during the workshop and come back to learn more :)

Installation

If you are working off your computer, for this tutorial, you’ll need to have

For Windows users

This guide will help: https://www.youtube.com/watch?v=antnsMgA4Ro (see the 2:50 min mark and stop at 3:50 min mark)

Not installed or using an online editor:

If you do not have this installed you can still follow along, please see this guide to setup an online editor: https://tinyurl.com/elixir-ideone

We will first open the terminal and try some things out:

If you have come from the online editor setup (i.e. from here: https://tinyurl.com/elixir-ideone), please skip ahead to: Step 1.1 Let’s Code!

If you are on mac, look for an app called “terminal”

If you are on windows, look for “command”

Now in the terminal, type:

`iex`

You’ll get something like this

This is where we will write some Elixir code.

IEx is the Interactive Elixir Shell which lets you write code that runs and outputs the results to you.

Step 1.1 Let’s Code!

Now let’s try the following snippet. Type the following and hit return/enter:

`IO.puts "hello"`

(Reminder: if you are using the online editor, you need to click on the “Run” button)

The output will look something like this

What’s happened here? We’ve printed out the word “hello” onto the screen.

Step 2: Data Types

In programming languages, types are a way to define something.

Let’s explain with an example. To represent someone’s name like “John Doe”, you would have this represented as characters as a String type.

For now, we will introduce you to these types:

• String
• Numbers
• Boolean

2.1 Strings

In the terminal, type the following

`name = "John Doe"`

Then underneath, type this

`IO.puts name`

You should get something like this

What just happened?

Firstly — we created a variable called “name”, which is used to represent in this case, a name.

Variables are used to store values for example, a person’s age or their name.

Next — we then assigned a value to name, in this case “John Doe”. If you notice, we wrapped John Doe around quotes, defines the variable as a String type.

After that — we then printed the name onto the screen, using `IO.puts` (a built-in Elixir function to print something onto the screen)

Next in the terminal, type the following (underneath):

`IO.puts "Hi #{name}"`

You should get

So we know that `IO.puts` prints something to the screen. The next thing you’ll notice is that within the quotes, the name variable is wrapped between `#{}`.

This is called String Interpolation — it’s a way to evaluate the value of a `variable` in a String.

Next type the following

`String.length(name)`

The result:

As you can see, the number of characters in name `8` was printed on the terminal.

In this case `length` is a function which counts the number of characters in a String.

What’s a function?

It’s code that has already been written to evaluate something. In our example above, the function `length` is available with `String` types to calculate the length of a defined `String`

Strings have other functions available, let’s try another:

`String.upcase(name)`

Result:

Now type this:

`String.starts_with?(name, "J")`

Result (`true`is returned to indicate that name starts with `J`):

Now try

`String.starts_with?(name, "A")`

As you can see, the result `false` indicates that name does not start with `A`

Try this:

`String.reverse("hello")`

For more String functions, check out https://hexdocs.pm/elixir/String.html

2.2 Numerical Data Types

Numeric Types are a way to represent numbers like whole numbers or numbers with a decimal point.

Let’s try this out, type:

`age = 30`

Then

`height = 170.5`

You should see the following:

You can see from the above that `age` is a whole number (30) which is known as an Integer numeric type, and `height` has decimal points (170.5), which is known as a Float numeric type.

Try the following:

`is_integer(age)`

You should get

Now try

`is_float(height)`

And then type

`is_integer(height)`

Both `is_integer` and `is_float` are a way to check the type of a variable

You can also use `interpolation` here too:

`IO.puts "John is #{age} and is #{height} cms tall."`

Now type this underneath and run the code to try some arithmetic:

`IO.puts age + 10IO.puts age * 2IO.puts age/2`

You can also do this:

`div(9,4) # divides 4 into 9 and returns the multiple i.e. 2rem(9, 4) # returns the remainder i.e. 1`

You can also leverage functions available to numerical types, for example Integer has a few functions you can try.

For example try:

`require IntegerInteger.is_even(2)Integer.is_odd(2)`

From the above, we need to `require` Integer to access the functions available.

For more functions available for Integer, check out https://hexdocs.pm/elixir/Integer.html

Exercise to try this yourself

• Create two `variables` ( `num1` and `num2`) and assign Integer values to them `15` and `20`
• Then find the maximum value out of `num1` and `num2` using this function https://hexdocs.pm/elixir/Kernel.html#max/2

2.3 Booleans

Remember the example above where we checked if a number was even or odd? The result was `true` or `false` , which are actually `Boolean` type.

`Booleans` are only either `true` or `false` and used for decision making in code. You’ll see further in the tutorial

Let’s try a few things:

`2 == 2`

In the example above `==` represents equality, when we want to check if the left and right of `==` equal each other

In this case, `2 == 2` is equal and result is `true`

Let’s try another

`5 == 9`

Now let’s try

`first = 1second = 2first == secondfirst != secondfirst < secondsecond >= first`

Here you can see a few things:

• `!=` checks to see that `first` does not equal `second`
• `<` checks that `first` is less than `second`
• `first` is greater than or equal to `second`

And operators:

`true && truetrue && falsetrue and false`

Or operators:

`true || truetrue || falsetrue or false`

2.4 Atoms

An `Atom` is a `constant` in Elixir, whose name is it’s own value. A `constant` cannot change in value.

For example

`:age:name`

These are used in Elixir for things like error handling (e.g. to know if an error occurred), attributes on more complex types (e.g. a person’s age) and more. We will explore these more further in the tutorial.

Step 3: Lists

A `List` is also a `Data Type` in Elixir and let’s us hold more than one value.

Let’s try this:

`names = ["Louise", "Tom", "Fred"]`

You should get the following

You can see from the above example, that we have created a `List` called `names` containing `Strings`

You can also join Lists too

`names ++ ["Judy", "Mac"]`

You can remove items from the list as well

`names -- ["Louise"]`

You can also store more than one data type (including other lists)

`list = [9, "ice cream", 3.5, [1, 2, 3]]`

`Lists` also have functions you can use, for example if you wanted to find the first and last elements of the list:

`List.first(list)List.last(name)`

Exercise:

• Try to find the number of items in a list, by using the `length` function
• Now try `List.replace(["a", "b", "c"], 1, "bb")` . What did you see? (Have a chat to your coach)

For more functions available to List, check out https://hexdocs.pm/elixir/List.html

Step 4. Maps

`Maps` is a `Data Structure` in Elixir and stores items with a `key` and a corresponding `value`

Let’s take a look at an example:

Here we are using a `Map` to represent a person with attributes `name`, `age` and `city` . These are also`Atoms` in the map, which are keys with corresponding values `Judy` , `27` and `Sydney`

Let’s try the following:

`person[:name]`

As you can see `name` is actually an `Atom` which is used to access the person `Map` , to find the value for the key`name`

You can also use the following syntax

`person.name`

Exercise:

• Now you try finding the age from person

Maps have functions you can use too. For example you can find the keys of a Map, type:

`Map.keys(person)`

You should get the following:

You can `merge`2 Maps together:

`job_details = %{occupation: "Scientist", industry: "Medical Science"}Map.merge(person, job_details)`

You should see both maps merged in the result

Exercise:

• Now create a new map named `car` with keys `model` , `colour` , `make` (for example, the value of `make` would be `toyota` , the value of `colour` would be `red` and the model would be `corolla`)
• Now use the `Map` function `put` to add another attribute with key `year` and value of `2010` (hint: see this API doc https://hexdocs.pm/elixir/Map.html#put/3)

Step 5: Pattern Matching

Pattern Matching is a technique that matches the left hand to the right hand of the `=` sign

In Elixir the `=` sign is actually a `match operator`

Let’s try this example

`a = 10`

You should get the following

Now type

`a`

You should get

This looks like we’re assigning `10` to the variable `a` .

However, what this is doing is matching the expression on the right to the left.

Let’s try this:

`x = 1^x = 2`

`^` is the pin operator — pattern matching on the variable `x` value rather than rebinding `x` (i.e. rather than re-setting the value of `x` )

Try this:

Another example:

`{x, y} = {10, 20}xy`

Example that does not match

`{y, 1} = {2, 2}`

With the above example, the left and right do not match. 1 does not equal to 2.

Let’s try another example:

`%{name: name, age: age} = %{name: "Penny", age: 42}`

Then, type

`age`

And then

`name`

You should get this

As you can see the there was a match between both sides

Now try this

`%{make: make} = %{make: "Toyota", colour: "red", model: "Corolla"}`

Then type

`make`

For the example above, we were still able to match, as `make` is a key in the map on the right.

The rest of the attributes are unassigned, as we were only interested in `make`

Let’s try another example

`[first | the_rest] = [1, 2, 3]`

Then type

`first`

Next type

`the_rest`

You should get this

You can see from the above, we were able to find the `first` element in the list and `the_rest` of the list by using pattern matching too.

Note: normally for this example, this is expressed as

`[head | tail] = [1, 2, 3]`

https://elixir-lang.org/getting-started/pattern-matching.html

https://www.tutorialspoint.com/elixir/elixir_pattern_matching.htm

Step 6. Conditionals

At times, you’ll need to have logic to determine what code to run in certain conditions.

6.1 If, else and unless conditions

Let’s try the following:

`if (10 > 1) do  trueend`

You should get something like this

What happened here? We checked if 10 was greater than 1, and if that was `true` we returned `true` .

Let’s try another example:

`names = ["Betty", "Roger", "Sean", "Kim"]`

Then type:

`if (length(names) == 4) do  IO.puts "There are 4 names"end`

You should get something like the following

In this example we checked that the number of names in the list was 4.

Using else:

Here is another example to try (be careful with brackets and syntax here)

`if (1 + 1 == 20) do  trueelse  falseend`

You should get something like this

What happened here?

We wanted to see if the result of `1 + 1 == 20`, because this was not true, the code followed the `else` condition.

Using Unless

Here is another example

`unless (1 + 1 == 20) do  trueend`

You should see the following

As you can see, the result of `1 + 1` is not `20` (it was false), and `unless` works by validating the `opposite` of the result is `true`

6.2 Case

We may want to check against a result against different cases, for example:

`num = 20case num do  1 -> IO.puts "equals 1"  11 -> IO.puts "equals 11"  20 -> IO.puts "equals 20"  _ -> falseend`

You should get the following:

Side note: The last case `_` is a `catch-all` where is nothing matches, the code will follow through to this case.

6.3 Cond

`cond` looks similar to `case` , however is a way to execute code like `if else`.

For example:

`cond do  num + 1 == 30 -> "Result is 30"  num + 1 == 40 -> "Result is 40"  num + 1 == 21 -> "Result is 21"end`

You should get the following

Step 7. Modules and Functions

Functions are a way to create code that can be re-usable.

Let’s try this

`sum = fn (a, b) -> a + b end`

Then type

`sum.(1,2)`

You should get:

This is an `anonymous function` , as the function does not have a name and is enclosed in `fn` and `end`

Modules

Functions can also live within a `Module`(which can contain many functions)

Let’s try the following

`defmodule Math do  def sum(a, b) do    a + b  endend`

You should get the following

The result means that a `Module` called `Math` was created.

The `Math` module has a `function` called `sum` which takes 2 `arguments` , `sum` is a `named function`

In the example, a `module`has the following syntax

`defmodule ModuleName do  # codeend`

And in this example, a `function`has the following syntax

`def function_name(arguments) do  # codeend`

Let’s try this

`Math.sum(10, 20)`

Using default values

Let’s redefine the `Math` `module`

`defmodule Math do  def sum(a, b \\ 0) do    a + b  endend`

Here we have given a `default` value in case `b` is not passed

Now type

`Math.sum(20)`

You should get:

Because `b` was not passed, the default value of `0` was used i.e. 20 + 0 = 20

Using Guards

You can also have functions to handle different cases.

Try the following:

`defmodule HelloList do  def find_first(list) do    List.first(list)  end  def find_first(list) when list == [] do    nil  endend`

In the example above, `when` is the `guard` condition, that checked if `list` is an empty list.

Now type

`HelloList.find_first([1,2])`

You should get

Now try

`HelloList.find_first([])`

You should have

( `nil` above means no value)

Using private functions

In functions you can have functions that are `private` to a `module` by defining this with `defp` instead of `def`

Let’s redefine the module

`defmodule HelloList do  def find_first(list) do    find_first_do(list)  end  def find_first(list) when list == [], do: nil  defp find_first_do(list) do   [first | _others] = list   first  end  end`

Here we’ve added a private function which uses `pattern matching` within the function to return the first element

Now type

`HelloList.find_first([1,2])`

You should get `1` as a result

Using Pattern Matching on Arguments

We can also use pattern matching on arguments of a function

Redefine the module again

`defmodule HelloList do  def find_first(list) do    find_first_do(list)  end  def find_first(list) when list == [], do: nil  defp find_first_do([first | _others]) do    first  end  end`

Now type

`HelloList.find_first([1,2])`

You should get `1` as a result

Step 8. Pipe Operator

Elixir also has something called a `Pipe Operator` which is used to pass the result of one function to another function.

Let’s try an example:

`Integer.to_string(12345)`

Notice that the result is a `String`

Now let’s pipe the result (now a `String` ) to another function

`Integer.to_string(12345) |> String.to_integer`

You should get the following:

You can see the result was converted back to an `Integer`

Let’s try another example

`String.split("hello", "") `

First we should get

Now let’s count the length of this list

`String.split("hello", "") |> length`

You should get

Exercise: Now try converting `hello` to uppercase using the `|>` and `String.upcase()`

That’s It!

You’ve reached the end of the tutorial, hope you’ve enjoyed the tutorial

Engineering Leader | Writer | Speaker | Traveller. Passionate about growing opportunities for people in Tech.

Engineering Leader | Writer | Speaker | Traveller. Passionate about growing opportunities for people in Tech.