Guard clauses enables us to check the arguments before executing the function. Guard clauses are usually preferred to if
and cond
due to their readability, and to make a certain optimization technique easier for the compiler. The first function definition where all guards match is executed.
Here is an example implementation of the factorial function using guards and pattern matching.
defmodule Math do def factorial(0), do: 1 def factorial(n) when n > 0: do: n * factorial(n - 1) end
The first pattern matches if (and only if) the argument is 0
. If the argument is not 0
, the pattern match fails and the next function below is checked.
That second function definition has a guard clause: when n > 0
. This means that this function only matches if the argument n
is greater than 0
. After all, the mathematical factorial function is not defined for negative integers.
If neither function definition (including their pattern matching and guard clauses) match, a FunctionClauseError
will be raised. This happens for this function when we pass a negative number as the argument, since it is not defined for negative numbers.
Note that this FunctionClauseError
itself, is not a mistake. Returning -1
or 0
or some other "error value" as is common in some other languages would hide the fact that you called an undefined function, hiding the source of the error, possibly creating a huge painful bug for a future developer.