Elixir Pattern Matching — The Pin Operator, Guards, and Match Expressions
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
Write a function that uses pattern matching to extract the first and last elements of a list.
Use guards to categorize an integer as "positive", "negative", or "zero".
Write a function
greet/1that matches on :morning, :afternoon, :evening atoms and returns appropriate greetings.Use
withto chain three operations that each return{:ok, result}or{:error, reason}.Implement a Fibonacci function using pattern matching on function heads.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro