Elixir: A Guide for Beginners
Tags: elixir, webdev, programming, beginners
Published: 2021-01-12 17:25:30

Learning a new programming language can be challenging. There’s frequently a load of novel code patterns, techniques, and features.

[Elixir](https://serokell.io/blog/introduction-to-elixir), in particular, is a bit odd. While its syntax looks easy, it can be surprising. Even more, the semantics – Elixir code can sometimes be a puzzle even if you are a functional programming veteran.

For this post, I have gone through all the basic Elixir constructs and collected a few tips that will help you write idiomatic Elixir code without hours of Googling. It should be useful when you attempt your first Elixir project or do [Exercism](https://exercism.io/) exercises. 

If you have already finished Exercism or done an Elixir course, you will know most of this already, but it might serve as a good refresher.

Now, let’s see what Elixir can offer us. 😉


## Basic syntax

Seriously, just watch this video. It's amazingly well done:

{% youtube pBNOavRoNL0 %}

Additionally, you can visit the [Basic Types](https://elixir-lang.org/getting-started/basic-types.html) and [Basic Operators](https://elixir-lang.org/getting-started/basic-operators.html) guides on Elixir’s webpage. 


## Pipes 

![Mario image that somehow ilustrates Elixir pipes](https://serokell.io/files/kc/kcyxakor.pasted_image_0_(8).png)
<center><i>Hop into the pipes, and they take you weird places.</i></center>

The pipe operator is the main operator for function composition in Elixir. 

It takes the result of the expression before it and passes it as the first argument of the following expression. Pipes replace nested function calls like `foo(bar(baz))`with `foo |> bar |> baz` .


### Example of pipes 

For example, here’s a function that makes a string into an exclamation sentence. 


```elixir
def exclaim(string) do
  string
    |> String.trim()
    |> String.capitalize()
    |> Kernel.<>("!")
end
```


Here, we take a string and pass it through various string methods. Since Elixir functions return the result of the last evaluated value, we don’t have to return anything. Data and transformations applied to it are clearly delineated. 

Pipes look even more incredible when you use them to compose higher-level functions. The whole program can easily be one large pipe, consisting of smaller pipes, which consist of even smaller pipes. Pipes all the way down!


## Higher-order functions

Elixir, as a functional programming language, makes heavy use of higher-order functions. 

Higher-order functions take in other functions as arguments. A few examples of HOFs are map, filter, and reduce.

![Higher-order functions](https://serokell.io/files/na/na14enxf.pasted_image_0_(9).png)
<center><i>Source: <a href='http://www.cse.chalmers.se/edu/year/2018/course/TDA452/lectures/HigherOrderFunctions.html'>http://www.cse.chalmers.se/</a> </i></center>

In Elixir, most of these functions live in the [Enum](https://hexdocs.pm/elixir/Enum.html) module of the standard library and operate on anything that is _[Enumerable](https://hexdocs.pm/elixir/Enumerable.html)_: lists, maps, ranges, etc. 

Using HOFs is a good substitute for loops: instead of a for-in, iterate over the elements of the list with `Enum.map`. 


```elixir
iex(1)> list = [1, 3, 5, 7]
[1, 3, 5, 7]

iex(2)> Enum.map(list, fn x -> x * x end)
[1, 9, 25, 49]
```


In the function above, we provided the square function as `fn x -> x * x end`. This is Elixir’s syntax for anonymous functions. There is also another way to provide functions, and that is through the _[capture operator](https://dockyard.com/blog/2016/08/05/understand-capture-operator-in-elixir)_. 


```elixir
iex(3)> Enum.map(list, &(&1 * &1))
[1, 9, 25, 49]
```


The `&` initiates a new anonymous function, while the `&1` denotes the first argument of that function. It’s debatable whether it makes it easier to read in this case. 😅


### Example of higher-order functions

Let’s say we need to read a list of lines from a file, make them into exclamation sentences, and print them out to the console. 

While we could usually do that with a loop, now we need another way. You can easily do it with a map function. 


```elixir
def exclaimify(file) do
  file
    |> File.read!()
    |> String.split("\n", trim: true)
    |> Enum.map(fn x -> exclaim(x) end)
    |> Enum.join("\n")
    |> IO.puts()
end
```


Remember the capture operator we talked about? Here, you can use it to make it simpler to call `exclaim`. Just switch up the map function to `Enum.map(&exclaim/1)`!


## Pattern matching 

![Elixir pattern matching meme](https://serokell.io/files/rr/rr7zgnre.pasted_image_0_(10).png)
<center><i>Sneakily borrowed from <a href='https://www.ludu.co/course/discover-elixir-phoenix/pattern-matching'>https://www.ludu.co</a></i></center>

Pattern matching is one of the most powerful parts of Elixir. Any kind of control flow that you write, it’s a reasonable heuristic to check whether you can’t do it with pattern matching. 

Basically, pattern matching works by providing some _pattern_ you expect in the data and then checking whether this pattern _matches_ the data.

It all starts with the match operator, `=`. Yup. In Elixir, we don’t assign variables; we _match_ them. 


```elixir
iex(1)> x = 1
1
```


What happens here is that Elixir checks whether the structure of both sides is the same. If it is, unassigned variables on the left side get assigned the values from the right side. If it is not, it will skip to the next match statement (or give you MatchError if the match wasn’t exhaustive).

We can play around with it in IEx. 


```elixir
iex(2)> 1 = 1
1

iex(3)> 1 = 2
** (MatchError) no match of right hand side value: 2

iex(5)> x = 1
1
```


It starts to get much more useful when the structures are more complex, enabling you to destructure tuples, lists, and nested maps quite easily.

**Tuples**

```elixir
iex(1)> {x, y} = {1, 3}
{1, 3}

iex(2)> x
1

iex(3)> y
3

iex(1)> {x, y} = {1, 2, 3}
** (MatchError) no match of right hand side value: {1, 2, 3}
```

**Lists**

```elixir
iex(1)> [head | tail] = ["Many", "years", "later", "as", "he", "faced", "the", "firing", "squad"]
["Many", "years", "later", "as", "he", "faced", "the", "firing", "squad"]

iex(2)> head
"Many"

iex(3)> tail
["years", "later", "as", "he", "faced", "the", "firing", "squad"]

iex(4)> [head | tail] = []
** (MatchError) no match of right hand side value: []

```

**Maps**

```elixir
iex(1)> %{age: x} = %{name: "Andrew", age: 27}
%{age: 27, name: "Andrew"}

iex(2)> x
27
```

Elixir’s [case statement](https://elixir-lang.org/getting-started/case-cond-and-if.html) operates on pattern matching – you can use it to match values or structures. 

``` elixir
def get_andrews_age(person) do
  case person do
    %{name: "Andrew", age: x} ->
      {:ok, x}
    %{name: _, age: _} ->
      {:error, :not_andrew}
    _ ->
      {:error, :not_person}
  end
end
``` 

In the case of the code above, we can go even more idiomatic. 


### Multiple function clauses 

Elixir supports pattern matching in function clauses. Therefore, instead of writing long-winded control flow statements, you can create multiple functions. 

```elixir
def get_andrews_age(%{name: "Andrew", age: x}), do: {:ok, x}
def get_andrews_age(%{name: _, age: _}), do: {:error, :not_andrew}
def get_andrews_age(_), do: {:error, :not_person}
```


Whoah! 🤯 If done correctly, this makes the code more extensible and easier to read. 


### Guards

But there’s even more modularity incoming. 

Let’s imagine we wanted to get the age only if Andrew is of legal age. We can easily accomplish this with a guard, an additional construction that augments pattern matching. 

Guards consist of `when` and a boolean expression, and are added to the function clause. They support only a limited set of expressions, which you can check out in the [documentation](https://hexdocs.pm/elixir/guards.html).

Here’s how our code would look with guards:

```elixir
def get_andrews_age_if_legal(%{name: "Andrew", age: x}) when x >= 18, do: {:ok, x}
def get_andrews_age_if_legal(%{name: "Andrew", age: x}) when x < 18, do: {:error, :not_legal_age}
def get_andrews_age_if_legal(%{name: _, age: _}), do: {:error, :not_andrew}
def get_andrews_age_if_legal(_), do: {:error, :not_person}
```



### Overloading functions

There’s this cool thing with Elixir functions where you can have functions with different argument counts, and they get counted as different functions even though they have the same name. In other words, you can overload functions via arity. 


```elixir
def add(x,y), do: x + y
def add(x,y,z), do: x + y + z 
```


One of these functions in Elixir-speak is actually add/2, the other is add/3. 

And, by using multiple functions and pattern matching, we can do a nice trick in our Elixir code that comes up quite often. 

We can define an x-argument function that will serve as the outer shell of our function. Then, we use that x-argument function to launch an x+1-argument function that will track the state as an argument, not a variable. 

It’s easier if you look at it in action. Here’s a function that reverses a list. 


```elixir 
def reverse(list), do: reverse(list, [])

def reverse([], list), do: 
def reverse([head | tail], reversed_list), do:
```

I have left the implementation details unfinished, and you might have a question or two about what is happening. Stay with me for a while. To fully explain it, we need to turn to recursive functions.  


## Recursive functions

![XKCD tail recursion](https://serokell.io/files/ko/kodpjcyk.pasted_image_0_(11).png)
<center><i>Source: <a href='https://xkcd.com/1270/'>xkcd</a></i></center>

A recursive function is simply a function that can call itself. 

One of the typical examples is calculating a certain Fibonacci number, a naive implementation of which in Elixir would be:

```elixir 
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(n-1) + fib(n-2)
```


(Take note of our use of pattern matching covered above.)

We can find two things in each sane recursive piece of code: a base case and a recursive call. 

*   **Base case.** This is the point where recursion terminates. It’s the result you want to achieve, and it will not call itself. 
*   **Recursive call.** This is where the program will do most of the computation. With it, you will reduce your task to a smaller one, and it will call itself. 

Basically, you can think of the base case as _the simplest possible example_ of the task you want to solve and the recursive call as _a way to simplify_ any given problem. In the example given, `fib(0)` and `fib(1)` are base cases, `fib(n)` is the recursive call.  


### Looping with recursion

In Elixir, we use recursion much more frequently than you would in a non-FP language since we don’t have access to things like loops. Recursion mimics imperative behavior quite well. 

For example, if we need to do something a set amount of times, we can create a recursive function with a number as an argument. 

Let’s say that we want to duplicate a string any given number of times. 

```elixir
def repeat(string, times), do: repeat(string, times, "")

def repeat(_string, 0, acc), do: acc
def repeat(string, times, acc), do: repeat(string, times - 1, acc <> string)
```


Instead of initializing a variable in the function, we add one more argument to the function. Then we call it again and again, each time decreasing `times` and increasing `acc`.

In the end, we have achieved the same result as instantiating two variables – `i` and `acc` – and creating a while loop that decrements `i` until it is equal to 0 and adds `string` to `acc` on each iteration. 

Now, I believe you will find it easy to complete the `reverse` example in the pattern matching section; it’s rather similar to `repeat` in structure.  

## Structs 

Structs are an upgrade of Elixir’s default key-value store, [maps](https://hexdocs.pm/elixir/Map.html). In contrast to maps, the keys of a struct are set when initializing, and you can’t add new ones afterwards.

Each module can have one and only one struct, and we tend to define it somewhere at the top of the module. 

```elixir 
defmodule Person do

  defstruct [:name, :age]

end
```

In the example above, I will now be able to access the struct with `%Person{}`. I can also provide some default values. For example, let’s assume that the default population I am dealing with, for some reason, is 27-year-old Andrews. 

```elixir
defmodule Person do

  defstruct [name: "Andrew", age: 27]

end
```

Now, calling the struct will give me a named map with the default values. 

```elixir
iex(1)> %Person{}
%Person{age: 27, name: "Andrew"}
``` 

The keys of the structs are not changeable once defined. Therefore, you won’t be able to add additional facts to the Person struct such as a home address, favorite Dostoevsky novel, and other tidbits that nobody needs to know about. 

But you can easily instantiate a struct with values other than default, update the values with the regular map functions, etc.

You can read more about structs [here](https://elixir-lang.org/getting-started/structs.html).


### Using structs

While creating a simple game in Elixir, you can, for example, create a struct that encodes the game state. Then, you provide functions that modify the game state in the module below. 

Exercism has a wonderful example in the [robot simulator](https://github.com/exercism/elixir/tree/master/exercises/robot-simulator) exercise. In it, you create a struct that holds the position and direction of a robot and provide various functions for moving and turning the robot. 

One could argue that the modules created like this, in a sense, function similarly to a class minus all the inheritance stuff (which we don’t do in Elixir, but there are ways to achieve behavior like that).


## What to learn next?

This knowledge (and a bit of Googling, I definitely didn’t cover everything) should be enough for you to go through [Exercism](https://exercism.io/) and be able to do simple programs in Elixir. 

If you want to continue learning after that, we have [a huge article](https://serokell.io/blog/learn-elixir) on all the different resources you can use. On the practical side, I recommend building a few toy projects for whatever interests you, perhaps looking into Phoenix. I mean, you know thyself better. 😉

Additionally, if you want to read more articles about Elixir and other fantastic languages, be sure to follow us on [Twitter](https://twitter.com/serokell). Good luck!