Pattern Matching Basics
How the = operator actually works in Elixir, and why pattern matching is the language's most fundamental idea.
3 min read
In most languages, = means "assign the right side to the left side." In Elixir, = is the match operator — it asserts that the left and right sides have the same shape, and binds any variables on the left to whatever values line up with them on the right.
The simplest case
x = 1This looks like ordinary assignment, and for a single variable it behaves that way — x is bound to 1. But the same mechanism scales up to much richer shapes.
Destructuring tuples
{a, b, c} = {1, 2, 3}
IO.puts(a)
# => 1
IO.puts(c)
# => 3Elixir compares the shape on the left ({a, b, c}) to the shape on the right ({1, 2, 3}) and binds each variable to its corresponding position. If the shapes don't match — different tuple size, for instance — you get a MatchError:
{a, b} = {1, 2, 3}
# ** (MatchError) no match of right hand side value: {1, 2, 3}That failure is a feature: it's Elixir refusing to silently proceed with data that isn't shaped the way your code expects.
Matching on literal values
The left side doesn't have to be all variables — literals in the pattern must match exactly:
{:ok, result} = {:ok, 42}
IO.puts(result)
# => 42
{:ok, result} = {:error, "not found"}
# ** (MatchError) no match of right hand side value: {:error, "not found"}This is exactly why the {:ok, value} / {:error, reason} convention from the previous lesson is so useful: you can pattern match directly against the outcome you expect, and get an immediate, loud failure if a function returns something else.
Destructuring lists
[first, second, third] = [10, 20, 30]More usefully, the head/tail pattern splits a list into its first element and the rest:
[head | tail] = [1, 2, 3, 4]
IO.puts(head)
# => 1
IO.inspect(tail)
# => [2, 3, 4]The | divides "the first thing" from "everything else" — this is the same pattern you saw reading command-line arguments in the previous section, and it's the foundation of how recursive list-processing functions work in Elixir.
Matching inside maps
Map patterns only need to specify the keys you care about — extra keys in the actual value are ignored:
user = %{name: "Ada", age: 30, role: "admin"}
%{name: name} = user
IO.puts(name)
# => AdaUsing _ to ignore values
An underscore matches anything without binding it, useful when you only care about part of a shape:
{:ok, _} = {:ok, "some result we don't need"}
[_first, second | _rest] = [1, 2, 3, 4]
IO.puts(second)
# => 2Why this matters so much
Pattern matching isn't a minor syntax convenience in Elixir — it's used everywhere: in function heads to select behavior based on argument shape, in case expressions to branch on results, and in with chains to short-circuit on failure. Getting comfortable reading {a, b} = something as "assert this shape and bind these names" rather than "assign" is the single most important mental shift when learning Elixir, and every lesson from here forward builds directly on it.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.