Pattern Matching in Elixir
Pattern Matching is Undoubtedely the most enticing feature of Elixir (and many other Programming Languages that supports it). In this blog I will try to explain how Elixir does Pattern Matching using example codes. There are essentially 3 different ways to do Pattern Matching in Elixir -
Pattern Matching Operator (=)
Equal (=) symbol in most programming language is considered as assigning a value to a variable. In Elixir, this is called Pattern Matching Operator instead.
In Python you can write
x = 5
This means we are assigning the value 5 to the variable x
You can write the same expression in Elixir, but the meaning is completely different. In Elixir this means we are matching the left-hand side i.e; x to the right-hand side i.e; 5. Following are the matching rules-
- Variables & values from the left-hand side is matched against values from the right-hand side.
- Variables can only be in the left-hand side of the expression.
- If there is a value in the left-hand side, it expects a matching value in the right-hand side.
Because of this the following statement will also compile in Elxir
5 = x
# As x was previously assigned to 5
# This evaluates to 5 = 5, and it matches
But this will fail because y was never set
100 = y
# You will get the following error
# ** (CompileError) iex:2: undefined function y/0
Note that variables can be re-assigned to another value
x = 100
x = %{:a => "apple"}
You can also match complex data structures like Tuples, Map, List etc.
{a, b, c} = {2, 3, 4}
# a is assigned to 2
# b is assigned to 3
# c is assigned to 4
%{:a => a, :b => b} = %{:a => "apple", :b => "ball", :c => "cat"}
# a will be assigned to "apple"
# b will be assigned to "ball"
[a, b, c] = [100, "200", 300]
# a will be assigned to 100
# b will be assigned to "200"
# c will be assigned to 300
[a | b] = [1, 2, 3, 4, 5]
# a will be assigned to 1
# b will be assigned to [2, 3, 4, 5]
Notice that you can destructure a list into it’s head and tail using Pattern Matching.
The following will fail, because 2 and 3 does not match 200 and 300 respectively.
{a, 2, 3} = {100, 200, 300}
# You will get the following error if you type this in Elixir REPL
# ** (MatchError) no match of right hand side value: {100, 200, 300}
Pattern Matching in Function arguments
Pattern matching are mostly useful in the context of defining functions. Let’s say you want to define a function that takes address data as input and format that into string. Let’s say your application represents address in two different ways-
- As a map containing :street, :postal_code and :city
- As a tuple {street, postal_code, city_key}.
You can implement this in the following way-
defmodule Form do
def format_address(
%{street: street, postal_code: postal_code, city: city}) do
format_address({street, postal_code, city})
end
def format_address({street, postal_code, city}) do
"""
#{String.upcase(street)}
#{String.upcase(postal_code)} #{String.upcase(city)}
"""
end
end
You can define two functions, one taking a map and one taking tuple, and it just works.
Pattern Matching in Case Statements
So far I have discussed two ways of pattern matching in Elixir, 1. By = operator, 2. In function arguments. But there is a third way of doing pattern matching as well : The case statements. Similar to C, C++ and Javascript switch-case statement (but more powerful), one can do pattern matching in case statements
case expr do
pattern1 -> value1
pattern2 -> value2
...
end
Let’s say you want to define a function in Elixir to return the first element from the list, if non-empty, and a default value if empty You can do so using case statement
def safe_head(lst, default) do
case lst of
[] -> default
[x | xs] -> x
end
end
First, the input list lst is matched against [], if matched it returns the default value, if not then lst is matched against the next pattern [x | xs], and now it will surely match because the list lst is non-empty, it will now return the first element x and we are done.
Conclusion
Thank you very much if you are still reading, and with this I will wrap this up. Happy Coding and Keep Learning 😄