Что такое guard expressions?

В Elixir guard expressions (предикаты охраны, выражения защиты) — это специальные логические выражения, которые можно использовать в определениях функций, case, cond и других конструкциях сопоставления с образцом (pattern matching) для дополнительной фильтрации по условиям. Они позволяют ограничить выполнение функции или ветки только в том случае, если заданное условие истинно.

Зачем нужны guard expressions

Elixir поддерживает мощное сопоставление с образцом, но иногда этого недостаточно. Например, если вы хотите вызвать функцию только если аргумент — положительное число или если список не пуст. В таких случаях guard выражения позволяют добавить более гибкие проверки, не ломая декларативный стиль Elixir.

Общий синтаксис

Guard выражения добавляются с помощью ключевого слова when:

defmodule Example do
def test(x) when is_integer(x) and x > 0 do
IO.puts("Положительное целое число")
end
def test(x) do
IO.puts("Что-то другое")
end
end

Здесь первая функция будет вызвана только если x — целое число и больше нуля.

Где можно использовать guard expressions

  1. В определениях функций (def, defp)

  2. В case, cond, with

  3. В receive-блоках

  4. В fn-анонимных функциях с несколькими ->

Примеры

Функция с guard

defmodule Math do
def describe_number(n) when is_integer(n) and n > 0 do
"Положительное целое число"
end
def describe_number(n) when is_integer(n) and n < 0 do
"Отрицательное целое число"
end
def describe_number(\_n) do
"Не целое число или ноль"
end
end

Использование в case

case number do
x when is_integer(x) and rem(x, 2) == 0 ->
IO.puts("Чётное число")
x when is_integer(x) ->
IO.puts("Нечётное число")
_ ->
IO.puts("Не число")
end

Поддерживаемые guard-функции

В guard можно использовать только ограниченное множество встроенных функций, поскольку они должны быть детерминированными и не вызывать побочных эффектов.

Список часто используемых guard-функций:

  • Проверка типа:

    • is_atom/1

    • is_binary/1

    • is_boolean/1

    • is_float/1

    • is_function/1

    • is_integer/1

    • is_list/1

    • is_map/1

    • is_nil/1

    • is_number/1

    • is_pid/1

    • is_port/1

    • is_reference/1

    • is_tuple/1

  • Операции сравнения:

    • \==, !=, >, <, >=, <=

    • \===, !== (строгие сравнения)

  • Арифметические:

    • +, -, *, /, rem, div
  • Логические:

    • and, or, not
  • Прочее:

    • byte_size/1

    • bit_size/1

    • tuple_size/1

    • hd/1, tl/1

    • map_size/1

    • length/1 (только в Elixir 1.12+)

Нельзя использовать в guard

  • Пользовательские функции

  • Функции из сторонних модулей

  • Вызовы с побочными эффектами (например, IO.puts, Enum.map)

  • Анонимные функции

Пример недопустимого guard:

def check(x) when Enum.empty?(x) do # ❌ Нельзя
...
end

Объединение и вложенность

Можно использовать and, or, not, но нельзя использовать ||, &&, ! — эти операторы применимы только в обычных выражениях.

Пример:

def validate(x) when is_integer(x) and x > 0 and rem(x, 2) == 0 do
:valid_even
end

Примеры с cond, with

cond с guard:

cond do
is_integer(x) and x &lt; 0 -&gt;
"Отрицательное число"
is_integer(x) and x == 0 ->
"Ноль"
is_integer(x) ->
"Положительное число"
end

with с guard:

with true <- is_integer(x),
true &lt;- x &gt; 10 do
IO.puts("Число больше 10")
end

Поведение при ошибках

Если guard выражение не проходит — функция с таким when просто не вызывается. Elixir проверяет альтернативные определения функции без ошибок. Если нет подходящей — будет FunctionClauseError.

Особенности guard в defguard (макросы охраны)

С версии Elixir 1.6 можно создавать переиспользуемые guard-предикаты через макрос defguard:

defmodule Guards do
defguard is_even(x) when is_integer(x) and rem(x, 2) == 0
end
defmodule Test do
import Guards
def check(x) when is_even(x), do: "Четное"
end

Это позволяет инкапсулировать логику проверки в одноименную функцию и использовать как обычную guard-часть.

Применение в практике

  • Валидация входных аргументов

  • Фильтрация данных

  • Безопасное разделение поведения функций

  • Повышение читаемости и надежности кода

  • Устранение избыточных if, case, cond в функциях

Guard expressions — мощный инструмент для управления ветвлением логики на основе простых и предсказуемых условий, без необходимости создавать сложные вложенные конструкции.