Что такое лямбда-функция в 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
Создание функции, которая возвращает другую лямбду — каррирование вручную.
Особенности и ограничения
-
Вызов через .() обязателен: нельзя вызывать лямбды как обычные функции.
-
Лямбды — это значения: можно присваивать, передавать, возвращать.
-
Ограничения по арности: нельзя перегружать лямбду с разным числом аргументов.
-
Без 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 — мощный инструмент для функционального, декларативного, модульного программирования. Они позволяют кратко описывать поведение, использовать их как данные и выстраивать сложные операции над коллекциями.