Discover Elixir & Phoenix

Back to All Courses

Lesson 3

Pattern matching

Pattern matching is one of the most important concepts in functional programming, and something you'll be using over and over again in your Elixir applications.

A standard Elixir app

So what is Pattern Matching?

One quirky thing about Elixir is that the =-sign doesn't always mean "assign this value to this variable name" (like in traditional programming languages), but it can also mean "match the left hand side with the right hand side".

Let's see how it works in an example:

iex(1)> x = 2
2

iex(2)> 2 = x
2

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

On the first line (x = 2), we assign the value 2 to x, as we would in any programming language.

On the second line (2 = x) though, the value (2) is on the left hand side of the =, and the variable name (x) is on the right hand side. In this case, we're not assigning a value, but we're using what's called pattern matching! Since we already know that x = 2, then obviously 2 = x is also correct, and it returns the value.

On the third line (3 = x), we use pattern matching again, only this time we match x against a value that it has not been assigned to before (3), which is why Elixir throws a MatchError!

The pin operator

As we just saw, variables are assigned a value when the left hand side is a variable name, and pattern matched when the left hand side is a value. It's worth noting that variables can also be reassigned at any point in your program, without giving any errors:

iex(1)> x = 2
2

iex(2)> x = 3
3

Sometimes however, we might not actually want to reassign the value! Say we have x = 3 and y = 4, and we want to pattern match x against y. In that case, we can't just type x = y, since that would reassign the value of x to y's value (4), as you can see in the example below:

iex(1)> x = 3
3

iex(2)> y = 4
4

iex(3)> x = y
4

In this case, we need to force the pattern matching behaviour by using the pin operator (^):

iex(1)> x = 3
3

iex(2)> y = 4
4

iex(3)> ^x = y
** (MatchError) no match of right hand side value: 4

By using the pin operator (^), you can make sure that you're not accidentally reassigning values when your intent was to pattern match.

Basic usage

Our examples so far have been simple matches between a variable name and an integer, but what really makes Elixir's pattern matching so powerful is that you can use it to match against any data type!

On very common use case is to use pattern matching in order to assign a tuple's values to different variable names:

iex(1)> {my_name, my_age} = {"Tristan", 24}
{"Tristan", 24}

iex(2)> my_name
"Tristan"

iex(3)> my_age
24

If we have the tuple {"Tristan", 24} but only want to pick up my_name, we can use the underscore character (_) to explicitly skip the other value:

iex(1)> {my_name, _} = {"Tristan", 24}
{"Tristan", 24}

iex(2)> my_name
"Tristan"

You could also make sure that the assignment of the age value to the variable my_age only happens if the other tuple values (in this case, the name) matches on both sides:

iex(9)> {"Bob", my_age} = {"Tristan", 24}
** (MatchError) no match of right hand side value: {"Tristan", 24}

iex(9)> {"Tristan", my_age} = {"Tristan", 24}
{"Tristan", 24}

iex(10)> my_age
24

Usage with "case"

Pattern matching in combination with case works in a similar way as switch-statements do in other languages. Notice how _ in this case acts as a "catch-all" at the end:

iex(1)> my_name = "Tristan"
"Tristan"

iex(2)> case my_name do
...(2)> "Tristan" ->
...(2)>   "That's the best name!"
...(2)> _ ->
...(2)>   "Still a good name!"
...(2)> end
"That's the best name!"

iex(3)> my_name = "Bob"
"Bob"

iex(4)> case my_name do
...(4)> "Tristan" ->
...(4)>   "That's the best name!"
...(4)> _ ->
...(4)>   "Still a good name!"
...(4)> end
"Still a good name!"

Make sure that you understand everything in this chapter well before moving on, as we'll be using these concepts a lot throughout the course!

In the next chapter, we'll create our very first Elixir program and you'll see how we can use pattern matching when defining functions.