Что такое 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
-
В определениях функций (def, defp)
-
В case, cond, with
-
В receive-блоках
-
В 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 < 0 ->
"Отрицательное число"
is_integer(x) and x == 0 ->
"Ноль"
is_integer(x) ->
"Положительное число"
end
with с guard:
with true <- is_integer(x),
true <- x > 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 — мощный инструмент для управления ветвлением логики на основе простых и предсказуемых условий, без необходимости создавать сложные вложенные конструкции.