Ujjawal Sinha logo
banner

Pattern Matching in Elixir

In computer science, pattern matching is the act of checking a given sequence of tokens for the presence of the constituents of some pattern.

date

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-

  1. Variables & values from the left-hand side is matched against values from the right-hand side.
  2. Variables can only be in the left-hand side of the expression.
  3. 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-

  1. As a map containing :street, :postal_code and :city
  2. 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 😄

icon-greek