Что такое лямбда-функция в Elixir и как она работает?

В Elixir лямбда-функция — это анонимная функция, которая создаётся с помощью специального синтаксиса fn ... -> ... end. В отличие от именованных функций, лямбда-функции не имеют имени и обычно используются там, где нужна временная логика, особенно при работе с перечислениями (Enum), фильтрацией, отображением, преобразованием данных и передачей функции как аргумента.

Синтаксис лямбда-функций

add = fn a, b -> a + b end
IO.puts(add.(2, 3)) # Выведет 5
  • fn a, b -> a + b end — определение анонимной функции.

  • add — переменная, содержащая лямбду.

  • Вызов лямбды осуществляется через .() — add.(2, 3)

Отличие от именованных функций

Характеристика Именованная функция Лямбда-функция (анонимная)
Имеет имя Да Нет
--- --- ---
Определяется внутри модуля Да (с def) Да (с fn ... end)
--- --- ---
Вызов Module.func(arg) func_variable.(arg)
--- --- ---
Аргументы по паттерну Да Да, но без перегрузки по арности
--- --- ---
Может быть передана как значение Да Да
--- --- ---

Примеры использования

1. Передача как аргумента

Enum.map(\[1, 2, 3\], fn x -> x \* x end)
\# => \[1, 4, 9\]

Здесь fn x -> x * x end — лямбда, переданная в Enum.map.

2. Сопоставление с образцом внутри лямбды

print_name = fn
{:ok, name} -> IO.puts("Имя: #{name}")
{:error, reason} -> IO.puts("Ошибка: #{reason}")
end
print_name.({:ok, "Алиса"})
print_name.({:error, "Не найдено"})

Можно использовать множественные сигнатуры с паттерн-матчингом — похоже на case, но внутри fn.

3. Лямбды с несколькими аргументами

compare = fn a, b ->
cond do
a > b -> :gt
a < b -> :lt
true -> :eq
end
end
compare.(5, 3) # => :gt

4. Комбинирование с Enum.reduce

Enum.reduce(\[1, 2, 3, 4\], 0, fn x, acc -> x + acc end)
\# => 10

Здесь лямбда принимает два аргумента: x — текущий элемент, acc — аккумулятор.

5. Композиция и каррирование (вручную)

multiply_by = fn factor ->
fn value -> value \* factor end
end
double = multiply_by.(2)
double.(5) # => 10

Создание функции, которая возвращает другую лямбду — каррирование вручную.

Особенности и ограничения

  1. Вызов через .() обязателен: нельзя вызывать лямбды как обычные функции.

  2. Лямбды — это значения: можно присваивать, передавать, возвращать.

  3. Ограничения по арности: нельзя перегружать лямбду с разным числом аргументов.

  4. Без guard-выражений в теле fn: только pattern matching, но не when.

Модульная альтернатива: &-синтаксис

Для кратких лямбд существует сокращённая запись:

Enum.map(\[1, 2, 3\], &(&1 \* &1))
\# То же самое, что: Enum.map(\[1, 2, 3\], fn x -> x \* x end)
  • &1, &2 — позиционные аргументы.

  • &Module.function/arity — передача ссылки на именованную функцию.

Пример:

Enum.map(\["a", "b", "c"\], &String.upcase/1)

Сравнение: fn -> vs &

Синтаксис Подходит для Пример
fn a -> ... end Любая логика fn a, b -> a + b end
--- --- ---
&(&1 + &2) Простые однострочные выражения &String.length/1
--- --- ---
&Module.func/2 Делегирование &Map.get/2
--- --- ---

Инкапсуляция поведения

Лямбды позволяют отделять логику от структуры данных. Вместо жёсткого if, case или ветвлений, можно передавать логику в качестве аргументов:

defmodule MyModule do
def apply_to_list(list, func) do
Enum.map(list, func)
end
end
MyModule.apply_to_list(\[1, 2, 3\], fn x -> x \* 10 end)
\# => \[10, 20, 30\]

Такой подход делает код гибким, расширяемым и тестируемым.

Инспекция и вывод

Лямбды имеют формат:

iex> my_fn = fn x -> x + 1 end
iex> is_function(my_fn)
true
iex> my_fn
#Function<... in :erl_eval.expr/5>

Можно проверить через is_function(fn_var, arity).

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