Что такое функциональное программирование: основы
Основы4 минРедакция Innovialabs
Коротко
Функциональное программирование строит программу из маленьких функций, которые принимают данные и возвращают новые, а сами функции передаются как значения. Вы описываете, что посчитать, а не как пройти циклом шаг за шагом.
Короткий ответ
Функциональное программирование (ФП) — стиль, в котором программа строится из функций: они принимают вход и возвращают результат, в идеале ничего не меняя вокруг себя. Вместо циклов, которые шаг за шагом меняют переменные, вы комбинируете маленькие функции: «взять список, оставить оплаченные заказы, достать суммы, сложить».
Специальный язык не нужен. JavaScript, Python, Kotlin, C# и многие другие поддерживают основные идеи ФП, а большая часть современного кода смешивает ФП с другими стилями.
Идея 1: функции — это значения
В языках с поддержкой ФП функции — первого класса: их можно сохранить в переменную, передать аргументом и вернуть из другой функции.
const greet = (name) => `Hello, ${name}`;
const actions = { greet };
console.log(actions.greet("Aziza"));
def greet(name):
return f"Hello, {name}"
say = greet
print(say("Aziza"))
Идея 2: функции высшего порядка
Функция высшего порядка принимает функцию аргументом или возвращает её. Так можно отделить что делать от того, как перебирать.
const multiplier = (factor) => (x) => x * factor;
const double = multiplier(2);
double(5); // 10
Возвращённая функция помнит factor — это называется замыкание.
Идея 3: map, filter, reduce
Эти три функции высшего порядка закрывают большую часть повседневной работы со списками:
| Функция | Что делает | Результат |
|---|---|---|
map | преобразует каждый элемент | список той же длины |
filter | оставляет элементы, прошедшие проверку | список короче или той же длины |
reduce | сворачивает все элементы в одно значение | одно значение |
Императивная версия
const orders = [
{ total: 120, paid: true },
{ total: 80, paid: false },
{ total: 50, paid: true },
];
let sum = 0;
for (let i = 0; i < orders.length; i++) {
if (orders[i].paid) {
sum += orders[i].total;
}
}
Функциональная версия
const sum = orders
.filter((o) => o.paid)
.map((o) => o.total)
.reduce((acc, t) => acc + t, 0);
То же на Python, где идиоматичнее использовать генераторные выражения:
orders = [
{"total": 120, "paid": True},
{"total": 80, "paid": False},
{"total": 50, "paid": True},
]
total = sum(o["total"] for o in orders if o["paid"])
В Python тоже есть map, filter и functools.reduce, но генераторные выражения и встроенные функции вроде sum обычно читаются лучше.
Императивный и декларативный стиль
- Императивный код описывает как: завести счётчик, пройти циклом, проверить, обновить.
- Декларативный код описывает что: оплаченные заказы, их суммы, итог.
Декларативный код обычно короче, в нём меньше мест для ошибок на единицу, и намерение видно в одной строке. Императивный бывает понятнее в сложных пошаговых алгоритмах или когда важна тонкая оптимизация.
Другие ключевые идеи
- Чистые функции: одинаковый вход всегда даёт одинаковый выход, без побочных эффектов. Их легко тестировать и понимать.
- Иммутабельность: вместо изменения данных создаётся новая версия. Это защищает от ошибок, когда одна часть кода незаметно меняет данные, на которые полагается другая.
- Композиция функций: большие функции собираются из цепочки маленьких, где выход одной — вход следующей.
Как начать применять ФП в обычном коде
- Заменяйте простые циклы
for, которые собирают новые списки, наmap/filterили генераторы списков. - Выносите вычисления в маленькие функции, которые используют только свои аргументы.
- Возвращайте новые объекты вместо изменения аргументов.
- Держите побочные эффекты (база данных, сеть, логи) на краях программы.
- Не перегибайте: если цепочка стала трудно читаться, разбейте её на именованные шаги.
Частые ошибки
- Использовать
mapпросто как цикл ради побочных эффектов — для этого естьforEachили обычный цикл. - Строить очень длинные цепочки, которые никто не может отладить.
- Забывать начальное значение в
reduce— в JavaScript это ломается на пустом массиве. - Считать, что ФП — это «всё или ничего». Смешивать стили нормально.