Как в Elixir реализованы условные конструкции: if, case, cond, with?
В Elixir условные конструкции реализованы функционально и декларативно, без привычных операторов switch, while или традиционного if-else в императивном стиле. Вместо этого используются конструкции if, unless, case, cond и with, каждая из которых заточена под определённые задачи сопоставления с образцом и ветвления логики.
if и unless
if
Это простейшая условная конструкция. Работает так же, как в других языках, но обязательно требует возвращаемое значение, так как всё в Elixir — выражение.
if 1 + 1 == 2 do
"Истина"
else
"Ложь"
end
Можно опустить else, если логика не требует альтернативной ветки.
unless
Обратная по логике к if. Выполняет блок, если условие ложно:
unless is_nil(value) do
IO.puts("Значение не nil")
end
if и unless — это макросы, а не встроенные ключевые слова, определённые в модуле Kernel.
case
case используется для сопоставления с образцом (pattern matching) и выбора ветки в зависимости от структуры данных или значения.
case {1, 2, 3} do
{4, 5, 6} ->
"Не совпало"
{1, x, 3} ->
"Совпало! x = #{x}"
_ ->
"Ни один шаблон не подошёл"
end
Каждая ветка case — это шаблон. Можно добавлять guard-выражения (when) для фильтрации:
case value do
x when is_integer(x) and x > 0 ->
"Положительное число"
_ ->
"Не integer или не больше нуля"
end
Если ни одно условие не совпадает — будет выброшено исключение CaseClauseError.
cond
cond — альтернатива вложенным if/else if. Проверяет список логических условий и исполняет первое истинное.
cond do
2 + 2 == 5 ->
"Математика поломана"
1 + 1 == 2 ->
"Работает"
true ->
"По умолчанию"
end
В отличие от case, здесь не сопоставляются шаблоны, а просто проверяются логические выражения.
Важно: если ни одно условие не истинно и нет последнего true, будет выброшено CondClauseError.
with
with используется для цепочки сопоставлений с образцом и вызова последнего блока, если всё проходит успешно. Это упрощает цепочки case или if с множественными проверками.
with {:ok, file} <- File.read("path/to/file"),
{:ok, json} <- Jason.decode(file) do
IO.inspect(json)
else
{:error, reason} -> IO.puts("Ошибка: #{reason}")
end
В этом примере:
-
Сначала читается файл.
-
Затем происходит декодирование JSON.
-
Если на любом шаге возникает ошибка — выполняется else.
Каждое выражение после <- должно возвращать сопоставляемый кортеж или значение.
Особенности
-
Все конструкции возвращают значение (как и всё в Elixir).
-
Нет циклов в привычном понимании — всё заменяется рекурсией, Enum, Stream.
-
Нет операторов break, continue — управление потоком достигается через pattern matching, хвостовую рекурсию и функциональные абстракции.
-
Условные конструкции используются как часть выражений, а не управляющие операторы как в императивных языках.
Когда использовать что
Конструкция | Когда использовать |
---|---|
if | Простое логическое условие |
--- | --- |
unless | Противоположное if, читается как "если не..." |
--- | --- |
case | Сопоставление с шаблонами |
--- | --- |
cond | Множество логических условий (аналог if...else if...) |
--- | --- |
with | Последовательность зависимых выражений с сопоставлением |
--- | --- |
Примеры
С if
defmodule Example do
def greet_user(name) do
if name do
"Привет, #{name}!"
else
"Привет, гость!"
end
end
end
С case и guard
defmodule Example do
def number_type(n) do
case n do
x when is_integer(x) and x > 0 -> "Положительное"
x when is_integer(x) and x < 0 -> "Отрицательное"
_ -> "Не integer или ноль"
end
end
end
С cond
defmodule Example do
def classify(age) do
cond do
age < 13 -> "Ребёнок"
age < 18 -> "Подросток"
age >= 18 -> "Взрослый"
end
end
end
С with
defmodule Example do
def get_user_name(id) do
with {:ok, user} <- DB.get_user(id),
true <- user.active do
{:ok, user.name}
else
_ -> {:error, "Пользователь не найден или неактивен"}
end
end
end