Skip to content

Elixir Pattern Matching — The Pin Operator, Guards, and Match Expressions

DodaTech Updated 2026-06-29 2 min read

In this tutorial, you will learn about Elixir Pattern Matching. We cover key concepts, practical examples, and best practices to help you master this topic.

Pattern matching is not a feature of Elixir — it IS Elixir. Almost every construct in the language relies on pattern matching: variable assignment, function dispatch, case expressions, and even data destructuring.

In this tutorial, you'll learn how pattern matching works at every level. Elixir uses the match operator = to bind variables and assert structure simultaneously.

What You'll Learn

  • The match operator (=)
  • Pattern matching on compound data types
  • The pin operator (^)
  • Guards in patterns
  • Pattern matching in function heads
  • case, cond, and with expressions

The Match Operator

# This is NOT assignment — it's matching!
x = 1  # Binds x to 1

# Both sides must match:
{1, x} = {1, 2}
IO.puts(x)  # => 2

# This raises a MatchError:
# {1, x} = {3, 4}  # ** (MatchError) no match of right hand side value: {3, 4}

Pin Operator

Use the pin operator ^ to match against an existing value instead of rebinding:

x = 5

# Without pin: rebinds x
x = 6
IO.puts(x)  # => 6

# With pin: matches against current value
x = 5
^x = 5  # Works (matches)
# ^x = 6  # MatchError!

# Practical: find in list
x = 3
[1, 2, ^x, 4, 5] = [1, 2, 3, 4, 5]  # Works

Pattern Matching in Function Heads

# Different function heads for different patterns
defmodule Math do
  def factorial(0), do: 1
  def factorial(n) when n > 0, do: n * factorial(n - 1)

  def length([]), do: 0
  def length([_ | tail]), do: 1 + length(tail)
end

IO.puts(Math.factorial(5))  # => 120
IO.puts(Math.length([1, 2, 3]))  # => 3

Guards

# Guards extend patterns with additional checks
defmodule Temperature do
  def describe(temp) when temp <= 0, do: "freezing"
  def describe(temp) when temp < 15, do: "cold"
  def describe(temp) when temp < 25, do: "warm"
  def describe(temp) when temp < 35, do: "hot"
  def describe(_), do: "very hot"
end

IO.puts(Temperature.describe(-5))  # => freezing
IO.puts(Temperature.describe(30))  # => hot

# Guard functions allowed: is_atom/1, is_binary/1, is_boolean/1, etc.
defmodule Guard do
  def process(x) when is_integer(x), do: "integer: #{x}"
  def process(x) when is_float(x), do: "float: #{x}"
  def process(x) when is_binary(x), do: "string: #{x}"
end

case

result = File.read("config.json")

case result do
  {:ok, content} ->
    IO.puts("Read #{byte_size(content)} bytes")

  {:error, :enoent} ->
    IO.puts("File not found")

  {:error, reason} ->
    IO.puts("Error: #{reason}")
end

with

with chains pattern matches — if any fail, the non-matching result is returned:

defmodule UserSetup do
  def setup(id) do
    with {:ok, user} <- find_user(id),
         {:ok, validated} <- validate(user),
         {:ok, saved} <- save(validated) do
      {:ok, saved}
    else
      {:error, :not_found} -> {:error, "User not found"}
      {:error, :invalid} -> {:error, "Validation failed"}
      error -> error
    end
  end

  defp find_user(id), do: {:ok, %{id: id, name: "Alice"}}
  defp validate(user), do: {:ok, user}
  defp save(user), do: {:ok, Map.put(user, :saved, true)}
end

IO.inspect(UserSetup.setup(1))

Practice Questions

  1. Write a function that uses pattern matching to extract the first and last elements of a list.

  2. Use guards to categorize an integer as "positive", "negative", or "zero".

  3. Write a function greet/1 that matches on :morning, :afternoon, :evening atoms and returns appropriate greetings.

  4. Use with to chain three operations that each return {:ok, result} or {:error, reason}.

  5. Implement a Fibonacci function using pattern matching on function heads.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro