Pattern matching


In Elixir, the equals signs ( = ) is called the "pattern matching operator". It is not the assignment operator, an important distinction to be made for imperative programmers. Its purpose is to match an expression on the right side to a pattern on the left. If you are coming from an object-oriented language, it may feel strange to disassociate ( = ) and assignment. However, this line of thinking is better suited to functional programming. After programming with pattern matching, it's unlikely you'll want to use a language without such a feature.

When Elixir encounters the "=" operator, it will attempt to make the left side match (be made equal to) the right side. In this way, Elixir treats the = sign much like the = sign from Algebra. The simplest example of this is 1 = 1 . Try typing this into an iex session. Elixir will make the match, both the left and right side are 1. Finally, it returns the value 1. The next simplest example of pattern matching is binding. You've already seen this before, a = 4. We have a variable to which we are binding a value. We must use the pattern, in this case the unbound variable "a", on the left side of the match operator. The expression or value should go on the right side. For example, 8 = b, we might assume this will bind 8 to the variable "b" but instead we'll get an error:

** (CompileError) iex:2: undefined function b/0

Elixir attempts to evaluate the expression "b", which has no value and thus we're returned the error. In order to correctly make the binding, we need to use the syntax pattern = expression. One of the strengths of pattern matching is that it isn't limited to only binding single variables. Check out the following examples:

# bind x to 10
iex(1)> x = 10
10
# bind a list of 3 variables to a list of 3 numbers
iex(2)> [a, b, c] = [1, 2, 3]
[1, 2, 3]
# bind the second element of a tuple
iex(3)> {:ok, value} = {:ok, 508}
{:ok, 508}
# bind the head and tail of a list
iex(4)> [head | tail] = ~w(this is a list of words)
["this", "is", "a", "list", "of", "words"]

# verify values
iex(5)> head
"this"
iex(6)> tail
["is", "a", "list", "of", "words"]
iex(7)> value
508
iex(8)> a
1
iex(9)> b
2
iex(10)> c
3
iex(11)> x
10

The second expression evaluated, denoted by iex(2), binds a list of variables to a list of numbers. It works because there are 3 variables and 3 values. Elixir is able to make the match by assigning a =1, b=2, and c=3.

The third expression is a bit different. Here we aren't passing in a variable in place of each value. Instead, we are matching on the first element and using a variable to bind to the second element. This is a common idiom in Elixir, binding on the return value of a tuple. You'll often see return values of {:ok, something} or {:error, reason}. These return values are purposefully crafted so that we can match on the case of success (ok) or failure (error) and respond accordingly. In our example, the atoms, ":ok" match and the variable "value" is bound to the element in the second position of the tuple, 508.

If you haven't seen the ~w sigil before it simply creates a list of words. The list of words, or letters even, should be separated by spaces and enclosed by parens. Technically, they can be enclosed an any of the following sigil delimiters, //, ||, "", '', (), [], {}, <> . For consistancy, I recommend using parentheses. The fourth expression simply matches the first element in the list to the variable "head" and the last element in the list to the variable "tail". We've seen this behavior before when we first explored Lists.

Multi-clause functions

We can have two or more implementations of the same function if the specification of their parameters is different. Consider the following "Drop" module. It calculates the velocity of a falling object based on the planet it's dropped from! I'm using the following example from Introducing Elixir, by David Eisenberg and Simon St. Laurent.

defmodule Drop do

  def fall_velocity(:earth, distance) do 
    :math.sqrt(2 * 9.8 * distance)
  end

  def fall_velocity(:moon, distance) do 
    :math.sqrt(2 * 1.6 * distance)
  end

  def fall_velocity(:mars, distance) do 
    :math.sqrt(2 * 3.71 * distance)
  end 

end

The "fall_velocity" function is defined three times, each with a different first parameter. Each function can share the same name because they differ in the specification of their parameters. They are, in fact, three distinct functions with different implementations. Elixir will pattern match the first parameter in order to know which function to run.

# Introducing Elixir. Example 3-1
iex(2)> Drop.fall_velocity(:earth, 20) 
19.79898987322333
iex(3)> Drop.fall_velocity(:moon, 20) 
8.0
iex(4)> Drop.fall_velocity(:mars, 20) 
12.181953866272849

Calling the fall_velocity function with the same distance for each location results in three different numbers, as we expected. Gravity is much stronger on Earth than it is on the moon or Mars.

Pattern matching is a powerful tool that helps keeps our code neat and succinct. It also helps us to write idiomatic, functional code. Next, we'll take a look at guard clauses and how they allow us to fine-tune pattern matching.

results matching ""

    No results matching ""