Multiple clause functions

Leap
Leap in Elixir
defmodule Year do
  @spec leap_year?(non_neg_integer) :: boolean
  def leap_year?(year) when rem(year, 400) == 0, do: true
  def leap_year?(year) when rem(year, 100) == 0, do: false
  def leap_year?(year) when rem(year, 4) == 0, do: true
  def leap_year?(_), do: false
end

In Elixir, functions can have multiple clauses. Which one will be executed depends on parameter matching and guards. When a function with multiple clauses is invoked, the parameters are compared to the definitions in the order in which they were defined, and only the first one matching will be invoked.

While in the operators approach, it was possible to reorder expressions as long as the suitable boolean operators were used, in this approach, there is only one correct order of definitions.

In our case, the three guards in the function clauses are as follows:

when rem(year, 400) == 0
when rem(year, 100) == 0
when rem(year, 4) == 0

But because of the order they are evaluated in, they are equivalent to:

when rem(year, 400) == 0
when rem(year, 100) == 0 and not rem(year, 400) == 0
when rem(year, 4) == 0 and not rem(year, 100) == 0 and not rem(year, 400) == 0

The final clause, def leap_year?(_), do: false, returns false if previous clauses are not a match.

Guards

The guards are part of the pattern-matching mechanism. They allow for more complex checks of values. However, because of when they are executed to allow the compiler to perform necessary optimization, only a minimal subset of operations is permitted. Kernel.rem/2 is on this limited list, and Integer.mod/2 is not. This is why, in this approach, only the first one will work, and the latter will not.

In this approach, the boolean operators matter too. Only the strict ones, not, and, or are allowed. The relaxed !, &&, || will fail to compile.

16th Oct 2024 · Found it useful?