Welcome. In this course on advanced pattern matching in Elixir, we start by using multiple function clauses to route different inputs to the correct logic. If you are new to this idea, think of it as matching the “shape” of data right in the function head. This builds on core Elixir fundamentals and prepares you for later topics like guards, case statements, and the pin operator.
Explanation:
- defmodule Calculator do … end defines a module named Calculator.
- Multiple def calculate clauses each match a different tuple pattern:
- {:add, a, b} returns a + b
- {:subtract, a, b} returns a - b
- {:multiply, a, b} returns a * b
- Division is handled with two clauses:
- {:divide, a, b} when b != 0 uses a guard to ensure b is not zero, then returns a / b.
- {:divide, _a, 0} matches divide-by-zero and returns an error tuple. The underscore in _a means “ignore this value.”
- Order matters. Elixir picks the first clause whose pattern matches and whose guard (if any) evaluates to true.
- The last three lines run the function:
- IO.puts prints the results of addition (8) and multiplication (28).
- IO.inspect prints the error tuple {:error, "Cannot divide by zero"} in a readable form.
This approach keeps the logic simple and readable. Instead of one function with many if/else statements, each operation becomes a clear, separate clause that matches a specific input shape.
Pattern matching in function heads is not limited to tuples. You can match on lists, maps, structs, and binaries, allowing for highly expressive and safe APIs.
Explanation:
- The base case
sum([])matches an empty list and returns 0, stopping recursion. - The recursive case
sum([head | tail])uses the[head | tail]pattern to split a non-empty list.headis the first element, andtailis the rest of the list. - This pattern is the standard way to traverse and process collections in functional programming.
Explanation:
- Map matching is "partial": the pattern
% {name: name}matches any map that has a:namekey, even if it contains dozens of other keys. - You can match literal values (like
:enor:es) alongside variables (likename). - The last clause
greet(%{name: name})acts as a fallback for any map that has a:namekey but doesn't match the specific languages above.
Explanation:
- Struct matching uses the
%ModuleName{}syntax. - Unlike maps, matching against
%User{}ensures that the input is specifically aUserstruct and not a plain map with the same keys. can_edit?(%User{role: :admin})matches only when the role is exactly:admin, whilecan_edit?(%User{})matches any otherUserstruct.
Explanation:
- Binaries (and strings) can be matched using the
<< >>syntax. <<"GIF8", _rest::binary>>matches any binary that starts with the literal string "GIF8".- The
::binarymodifier tells Elixir that_restcan be any sequence of bits/bytes of any length following the prefix. This is commonly used for parsing file headers or network protocols.
If no function clause matches, Elixir raises a FunctionClauseError. To avoid this, provide a catch-all clause to handle unexpected inputs safely.
Order matters: Elixir matches clauses from top to bottom. More specific patterns (or those with guards) should come before more general ones.
Elixir matches the structural pattern first; if it matches, the guard is then evaluated. When patterns overlap, order still decides which clause is tried first; a failing guard causes Elixir to continue trying later clauses.
Guards can make clauses more precise, but they don’t change which clause is tried first—order still governs which structural pattern is tested before guards are evaluated.
You learned how to:
- Match tuple shapes in function heads to dispatch logic (Calculator).
- Use guards to protect clauses (b != 0) and add safe fallbacks for invalid inputs.
- Reason about clause order and overlapping patterns; Elixir matches structure first, then evaluates guards, and continues to later clauses when a guard fails.
- Match lists, maps, structs, and binaries in function heads.
This pattern is common in real Elixir codebases because it is fast to read, safe, and extensible. Ready to solidify this skill? Head over to the practice section and try it out.
