Yianna Kokalas

Pattern Matching in Elixir

19 Mar 2017

Pattern matching in elixir is right associative which means it evaluates expressions right to left.

# Left associative
1 + 1 + 2

# Right associative
1 + (1 + 2)

Then it matches from the left to the right. If there is no MatchError it will assign value to any bindable variables from left to right.

You cannot bind a variable on the right hand side with data on the left hand side pattern matching.

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

iex(4)> other_number = 0
0
iex(5)> other_number = number
13
iex(6)> other_number
13

iex(12)> 1 = unassigned_variable
** (CompileError) iex:12: undefined function unassigned_variable/0

Elixir has support for immutability when you don’t want to bind to a variable. We use the pin ^ operator to make a variable immutable. This is useful when you need to enforce a pattern match and don’t want assignment.

iex(1)> favorite_food = :vegetables
:vegetables
iex(2)> ^favorite_food = :meat
** (MatchError) no match of right hand side value: :meat
    
iex(3)> cheese = :munster
"munster"
iex(4)> ^cheese = :swiss
** (MatchError) no match of right hand side value: :swiss
    

The assignment of rhs to lhs within the context of pattern matching only happens with mutable variables. So if you pattern match a pinned variable then it will only match not assign. You can see here that cheese is :munster so the first expression does not return MatchError and the second expression does return a MatchError since we’re trying to match it with :cream_cheese.

iex(5)> {:ok, ^cheese} = {:ok, :munster}
{:ok, :munster}

iex(14)> {:ok, ^cheese} = {:ok, :cream_cheese}
** (MatchError) no match of right hand side value: {:ok, :cream_cheese}
    
iex(14)>

You can also ignore binding after matching with an underscore _. When using _ alone we will get a CompileError unless it’s used during matching. When we use an underscore for matching it will ignore the type of data that’s in that slot on the right hand side. When prefixing a variable with _ binding happens but you will recieve a warning that “the variable should be ignored”, as a side affect.

ex(2)> _
** (CompileError) iex:2: unbound variable _
    
iex(2)> _favorite_food = :cheese
:cheese
iex(3)> favorite_food
:vegetables
iex(4)> _favorite_food
warning: the underscored variable "_favorite_food" is used after being set. A leading underscore indicates that the value of the variable should be ignored. If this is intended please rename the variable to remove the underscore
iex:4

:cheese
iex(5)> 

Pattern Matching Collections (Tuples, Lists and Structs)

Tuples

Pattern matching is useful for breaking down complex types of data.

iex(13)> {animal, vegetable, number} = {"dog", :carrot, 100}
{"dog", :carrot, 100}
iex(17)> animal
"dog"
iex(18)> vegetable
:carrot
iex(19)> number
100
iex(20)>

When there is no match in the number of elements from rhs to lhs there will be an error.

iex> {animal, vegetable, number} = {"dog", :carrot}
** (MatchError) no match of right hand side value: {"dog", :carrot}
   
iex> {animal, vegetable, 1} = {"dog", :carrot, 99}     
** (MatchError) no match of right hand side value: {"dog", :carrot, 99}

Also it will fail if the collection type is different.

iex> {animal, vegetable, number} = ["dog", "carrot", 100]
** (MatchError) no match of right hand side value: ["dog", "carrot", 100]
    

When pattern matching collections the number of elements and collection type must match otherwise we will recieve a MatchError. Unless we prevent default behavior by using _ or the pin operator ^ then Elixir will try binding to a variable.

iex> {:ok, message} = {:ok, "food is good!"}
{:ok, "food is good!"}

iex> {:ok, message} = {:error, "food is good!"}
** (MatchError) no match of right hand side value: {:error, "food is good!"}
    
iex> {:ok, message} = {:ok, "food is good!", 100}
** (MatchError) no match of right hand side value: {:ok, "food is good!", 100}

iex> {:ok, message, _} = {:ok, "food is good!", 100}
{:ok, "food is good!", 100}

iex> {:ok, email} = {:ok, body: "Let's get dinner!", sender: :susie@gmail, receiver: :bob@gmail
{:ok, [body: "Let's get dinner!", sender: :susie@gmail, receiver: :bob@gmail]}

iex> email[:body]
"Let's get dinner!"

iex> email[:sender]
:susie@gmail

iex> {:ok, email} = {:ok, [body: "din din!", sender: :susie@gmail, receiver: :bob@gmail]}
{:ok, [body: "din din!", sender: :susie@gmail, receiver: :bob@gmail]}

iex> email[:body] 
"din din!"

iex> {:ok, ^email} = {:ok, "Dinner is ready!"}
** (MatchError) no match of right hand side value: {:ok, "Dinner is ready!"}

Lists

Lists are amazingly useful data structures and have many abilities, including [head | tail] syntax. What makes this work is the cons operator | which assigns the first item in the list to the variable on the left and assigns the rest to the variable on the right.

Bonus: This technique will assign any arbitrary length array to tail as long as there is an element for head

iex(45)> [animal, vegetable, number] = ['dog', 'carrot', 1]
['dog', 'carrot', 1]
iex(46)> [head | tail] = [0, 1, 2 ,3]
[0, 1, 2, 3]
iex(47)> [h | t] = [0, 1, 2 ,3]      
[0, 1, 2, 3]
iex(48)> [foo | bar] = [0, 1, 2 ,3]
[0, 1, 2, 3]
iex(49)> head
0
iex(50)> h
0
iex(51)> foo
0
iex(52)> 

If the list on the right hand side is empty it will result in an error.

[h | t] = []
** (MatchError) no match of right hand side value: []

If that wasn’t cool enough, you can prepend things to a list using [h | t].

iex(64)> queue = [:sam, :sarah, :winston]
[:sam, :sarah, :winston]
iex(65)> queue = [:walter | queue]
[:walter, :sam, :sarah, :winston]
iex(66)> 

Pattern Matching with _

Using _ is almost like a wildcard that won’t bind anything at all. Whatever we point to _ won’t be bound.

iex(6)> [h | _] = [1, 2, 3]
[1, 2, 3]
iex(7)> h
1
iex(8)> [_ | t] = [1, 2, 3]
[1, 2, 3]
iex(9)> t
[2, 3]
iex(10)> 

iex(27)> {:ok, message, _} = {:ok, "hey what's cooking?"}             
** (MatchError) no match of right hand side value: {:ok, "hey what's cooking?"}
    
iex(27)> {:ok, message} = {:ok, "hey what's cooking?", :susie@home}   
** (MatchError) no match of right hand side value: {:ok, "hey what's cooking?", :susie@home}
        
iex(27)> {:ok, message, _} = {:ok, "hey what's cooking?", :susie@home}
{:ok, "hey what's cooking?", :susie@home}
iex(28)> 

Lists in a Tuple

You can also combine a tuple with a list on the right, match on :ok and bind to a variable of message.

Imagine you have a complex structure like this, {:ok, ["Let's get dinner!", sender: :susie@gmail, receiver: :bob@gmail]}.

How can you quickly assign that message to a variable to access it’s data?

iex(42)> {:ok, message} = {:ok, [body: "Let's get dinner!", sender: :susie@gmail, receiver: :bob@gmail]}
{:ok, [{:body, "Let's get dinner!"}, {:sender, :susie@gmail}, {:receiver, :bob@gmail}]}
iex(43)> message[:sender]
:susie@gmail
iex(44)> message[:receiver]
:bob@gmail
iex(45)> 

Pattern Match with String concat on left hand side.

You can also use the concat <> operator to join a string with a variable that is getting assigned value with pattern matching.

"super" <> rhs = "super-duper"
"super-duper"
iex(19)> rhs
"-duper"

Conclusion

Pattern Matching in Elixir is EXTREMELY powerful and an amazing feature for a functional programming language to use. I hope this tut helped you. Thanks for reading.

Tweet me @yonk_nyc if you like this post.

Tweet