Elixir Fundamentals: Functional Programming and OTP · Functional Elixir: Data, Pattern Matching and Functions

Pattern Matching: The = Operator Is a Match

In Elixir, = matches a pattern against data and binds variables. Destructure tuples, lists and maps, use the pin operator ^ to match an existing value, and handle {:ok, value} and {:error, reason} results.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A response handler that pulls data out of tuples, lists and maps with patterns, and reacts differently to {:ok, ...} and {:error, ...} results.

The idea

= is the match operator. Elixir checks that the shape on the left matches the data on the right, binding any variables:

{:ok, user} = {:ok, "ana"}       # user = "ana"
[first | rest] = [1, 2, 3]       # first = 1, rest = [2, 3]
%{name: name} = %{name: "Ana", age: 34}   # partial map match

If the shapes don't match, you get a MatchError. That's a feature: code asserts the shape it expects.

  • _ ignores a value: {_, count} = {:ignored, 3}.
  • The pin operator ^x matches against the current value of x instead of rebinding it.
  • Elixir's convention for results is tagged tuples: {:ok, value} or {:error, reason}. Pattern matching makes handling both cases natural.

Patterns appear everywhere: function heads, case, with, and receive.

The code

Destructuring, the pin operator and tagged tuples
{:ok, user} = {:ok, %{name: "Ana", role: :admin, langs: ["Elixir", "Go", "Rust"]}}
%{name: name, langs: [first_lang | other_langs]} = user
IO.puts("#{name} likes #{first_lang} (and #{length(other_langs)} more)")

{status, code, _headers} = {:http, 200, ["content-type: json"]}
IO.puts("#{status} #{code}")

expected = 200
case {:http, 404} do
  {:http, ^expected} -> IO.puts("as expected")             # ^ matches the current value of `expected`
  {:http, other} -> IO.puts("unexpected status #{other}")  # `other` binds anything else
end

results = [{:ok, 3}, {:error, :timeout}, {:ok, 5}, {:error, :not_found}]
for result <- results do
  case result do
    {:ok, value} -> IO.puts("ok: #{value}")
    {:error, :timeout} -> IO.puts("retrying after timeout")
    {:error, reason} -> IO.puts("failed: #{reason}")
  end
end

try do
  {:ok, _} = {:error, :boom}
rescue
  e in MatchError -> IO.puts("MatchError: #{inspect(e.term)}")
end

Output

Ana likes Elixir (and 2 more)
http 200
unexpected status 404
ok: 3
retrying after timeout
ok: 5
failed: not_found
MatchError: {:error, :boom}

Run this on your computer — see “Get Started” for setup.

Common mistakes

  • Thinking = is assignment. It's a match; the left side is a pattern.
  • Rebinding by accident inside a pattern when you meant to compare; use ^var.
  • Over-specific matches on maps: %{a: x} matches maps with at least key :a, which is usually what you want.
  • Ignoring {:error, _}. Handle both shapes, or let it crash deliberately (see the OTP lessons).

Key points

  • = matches shapes and binds variables; mismatches raise MatchError.
  • Destructure tuples, lists ([head | tail]) and maps (partial matches).
  • ^ pins an existing value; _ ignores.
  • Tagged tuples {:ok, v} / {:error, r} are the result convention.

Further reading: Pattern matching.

Practice questions

What does the pin operator ^ do?

Answer

In a pattern, ^x matches against the current value of x instead of rebinding x to whatever is on the right side.

What does [head | tail] = [1, 2, 3] bind?

Answer

head is 1 and tail is [2, 3]. The | splits a list into its first element and the remaining list.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?