Pattern Matching in Elixir: Five Things to Remember

Anna Neyzberg ·

Elixir has been getting a lot of attention these days for being such a powerful language. It is highly-concurrent, fault-tolerant, and scalable.

When beginning to learn Elixir, you quickly come upon the term Pattern Matching.
What is Pattern Matching and more importantly what do you need to know?

1) It’s Not Assignment

In Elixir there is no real concept of assignment as you might be used to in other programming languages (Ruby, Javascript, Java, etc.). The “=” is a match operator. It allows us to compare the right side of an expression(known as the “term”) against the left side (known as the “pattern”) to see if they “match”.

Let’s take a look in more detail:

  iex(6)> a = 1
  1
  iex(7)> b = 2 
  2

If the examples above were from an imperative language, we could assume that we have a variable on the left and the value on the right and an assignment operator “=.”

Given there is no assignment operator – what is happening?

When a new variable is being initialized(as seen above), the following happens:

  • The term on the right is evaluated (either “1” or “2”)
  • The resulting value is matched against the left-side pattern (“a” or “b”)
  • The variables on the left-hand side are initialized and bound to the values on the right-hand side.

In Elixir we would say “a” is bound to “1” and “b” is bound to “2”.

Let’s take a look at another example:

  iex> 2 = b
  2 #huh?

What is happening here? This does not look like something we would normally see in an imperative language.

Looking above we know that “b” has a value of “2”. The term on our right (the value of b) is being matched to the pattern on the left-hand side “2”.

The term matches and so the value on the right-hand side is returned.

Yet what happens when the term doesn’t match?

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

Remember that “a” has a value of “1”. As the pattern is evaluated, the term on the right does not match the pattern on the left and we receive an error.

2) Variables are Bound and Rebound

As mentioned, in Elixir we cannot assign values to variables. However, we can bind a variable to a value. If a variable already has a value, we can rebind that value.

Here “b” is being rebound from a value of “2” to a value of “3″:

iex(8)> b = 2
2
iex(9)> b = 3 
3

The last thing to note about variables is the pin(^) operator:

When using the pin operator we are matching against the contents of the variable rather than binding the variable to the term on the right-hand side.

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

iex(3)>
[/code]

In this case the term 4 does not match the pattern 3 (the value of the pinned variable ^b), we receive an error.

3) Matching in data structures.

Using pattern matching in data structures is common in Elixir:

In the example below, the untyped data structure with the curly braces and containing comma-delimited elements is called a Tuple. Tuples are generally used to group a fixed number of elements.

This example assumes that the right-hand term is a tuple with two elements. Notice that the left-hand pattern is a tuple with two variables.

iex(12)> {result, value} = {:ok,2}
{:ok, 2}
iex(13)>

The element with the value “:ok” is an atom. Atoms are literal constants in Elixir. They look like symbols for those familiar with Ruby or C/C++.

What do we imagine will happen above with what we know about pattern matching?

We know that Elixir is going to match the term on the right to the pattern on the left.
In this case, the variables “result” and “value” will each be bound to their respective values on the right-hand side.

As we can see above, once the variables are bound, the term on the right is the return value.

If we set the first element in the term as an atom “:ok” and leave the second item, we can see that the variable “value” is bound to the respective element in the right-hand term.

iex(12)> {result, value} = {:ok,2}
{:ok, 2}
iex(13)> value
2

What happens when the pattern does not match?

iex(12)> {:ok, value} = {:notok, 2}
** (MatchError) no match right hand side value: {:notok,2}

Above we can see that the first element in the tuple on the right-hand side does not match the first element in the tuple on the left – and so we receive an error.

It is a common pattern in Elixir for functions to return either { :ok, result } or { :error, reason }.

4) Multi-Clause Functions

A multi-clause function is a function that has multiple definitions with the same arity (number of arguments). Pattern matching is very powerful when used in combination with multi-clause functions.

Let’s create a “Greeter” module. In Elixir we use modules to group together functions:

# greeter.exs
defmodule Greeter do
  def hello(:jane), do: "Hello Jane"
  
  def hello(name) do
    "Hello #{name}"
  end
end

We have two definitions for the function “hello”. Notice that each function accepts a different argument. The function that is executed is the one where the arguments that are passed in match the pattern of the arguments in that function’s signature.

iex(9)> Greeter.hello(:jane)
"Hello Jane"
iex(10)> Greeter.hello(:anna)
"Hello anna"

What are the benefits here?
Pattern matching forces us to be very intentional about our data, since each function takes a specific signature.
It also allows for more atomic code – the ability to break functions into small pieces.
It prevents the need for complicated conditional logic.

5) Recursion

In Elixir we think about looping differently than we might in imperative languages like Ruby or Javascript. Looping constructs like ‘while’ or ‘do while’ are not provided. The way we accomplish looping in Elixir is through recursion.

Let’s take a look at recursion using lists.

This data structure below that looks like what might be an array in Ruby or Javascript is actually a singly linked list. Elixir allows you to reference the first element in the list as the “head” and the rest of the list as the “tail”.

Here we have the “Calculator” module:

# calculator.exs
defmodule Calculator do
  def multiply_by_two([]), do: return []

  def multiply_by_two([head | tail]) do 
    [head * 2 | multiply_by_two(tail)]
  end
end

We have two functions with the same arity. One returns a list with each item in the list multiplied by 2. The other returns an empty list.

iex> Calculator.mulitply_by_two([])
[]

iex> Calculator.multiply_by_two([1, 2, 3])
[2, 4, 6]

When we call our Calculator function with a list, notice that we multiply the head of the list by 2. Then we call the function “multiply_by_2” on the tail of the list. It will continue iterating through each item in the list until the list is empty.

Once the list is empty, that empty list will match against the function that takes an empty list as an argument. That function will execute and the recursion will stop. Recursion is often how we iterate over lists in Elixir.

And that’s it – the basics of pattern matching in Elixir.

In summary:

  1. It’s not assignment
  2. Variables are bound and rebound
  3. Pattern matching in data structures is common
  4. Use pattern matching with multi-clause functions
  5. Recursion allows for looping.