Multi-Clause Functions
A function in Elixir can have several clauses with the same name and same arity. Each clause describes a pattern. When the function is called, Elixir walks the clauses top to bottom and runs the first one whose pattern (and guard, if any) matches the arguments.
Here is the smallest example:
iex> defmodule Traffic do
...> def light(:red), do: "stop"
...> def light(:yellow), do: "slow down"
...> def light(:green), do: "go"
...> end
iex> Traffic.light(:red)
"stop"
iex> Traffic.light(:green)
"go"
Each light/1 clause matches a specific atom. There is no if, no
case, just three small functions with the same name that differ in
what they accept.
Order Matters
Elixir picks the first matching clause, so a broad pattern placed above a narrow pattern will swallow the narrow one:
iex> defmodule Reply do
...> def message(_), do: "unknown" (1)
...> def message({:ok, _}), do: "success"
...> end
iex> Reply.message({:ok, 42})
"unknown"
| 1 | The catch-all clause matches everything, so the {:ok, _} clause
is never reached. Fix it by putting the specific clause first. |
| Put specific patterns on top, catch-all patterns at the bottom. |
Combining With Guards
Clauses can have guards. This is one of the most common shapes you will see in real Elixir code:
iex> defmodule Law do
...> def can_vote?(age) when is_integer(age) and age >= 18, do: true
...> def can_vote?(age) when is_integer(age), do: false
...> def can_vote?(_), do: raise ArgumentError, "age must be an integer"
...> end
iex> Law.can_vote?(18)
true
iex> Law.can_vote?(16)
false
iex> Law.can_vote?("18")
** (ArgumentError) age must be an integer
Default Arguments
You can give a parameter a default value with \\. When a function
with a default value has several clauses, Elixir requires you to
declare the default in a bodyless function head above the clauses:
iex> defmodule Greeter do
...> def hello(name, greeting \\ "Hello") (1)
...>
...> def hello(name, greeting) when is_binary(name) do
...> "#{greeting}, #{name}!"
...> end
...>
...> def hello(_name, _greeting) do
...> "I don't know who that is."
...> end
...> end
iex> Greeter.hello("Alice")
"Hello, Alice!"
iex> Greeter.hello("Bob", "Hi")
"Hi, Bob!"
iex> Greeter.hello(42)
"I don't know who that is."
| 1 | The bodyless head declares the default. The real work happens in the clauses below. |
Arity Is Part of the Identity
Two functions with the same name but different arity are two
different functions. Elixir writes them as name/arity:
iex> defmodule Greeting do
...> def greet(), do: "Hello, world!"
...> def greet(name), do: "Hello, #{name}!"
...> def greet(name, time), do: "Good #{time}, #{name}!"
...> end
iex> Greeting.greet()
"Hello, world!"
iex> Greeting.greet("Alice")
"Hello, Alice!"
iex> Greeting.greet("Bob", "morning")
"Good morning, Bob!"
This has nothing to do with pattern matching, but you will see it alongside multi-clause functions all the time, so it is worth keeping the two ideas apart in your head.
Unused Parameters
If a clause does not use one of its parameters, prefix the name with
_ so the compiler does not warn about it:
def log({:ok, _value}), do: :ok
def log({:error, _reason}), do: :error
Both _value and _reason act as wildcards but document what would
have been there. This is pure style: the compiler does not care, the
next reader will.