Принципы функционального программирования: почему это важно
Рассказываем о принципах функционального программирования: какие у него минусы, и какие языки относятся к функциональным.
Основные концепции
Функциональное программирование основывается на нескольких важных концепциях: отсутствие побочных эффектов и изменяемых данных, чистые функции и их композиция. Давайте рассмотрим каждую из них.
Чистые функции
Чистая функция максимально проста. Она должна всегда возвращать один и тот же результат. Посмотрите на эту JavaScript-функцию:
var z = 10; function add(x, y) { return x + y; }
Конечно, это искусственный пример, но он очень хорошо демонстрирует, как должна работать чистая функция. При одних и тех же значениях x и y мы всегда будем получать одинаковый результат функции. Предсказуемость – важная часть работы программы в функциональном программировании.
Изменяемые данные и побочные эффекты
Вернемся к примеру кода. Если мы добавим в качестве аргумента функции add(), переменную z, которая объявлена выше, наша функция перестанет быть чистой и предсказуемой. Почему? Потому что z объявлена как обычная переменная: она доступна для изменения из любого места программы.
Идея неизменности в функциональном программировании заключается в том, что переменные не могут быть изменены после объявления.
Корректный код чистой функции с z должен выглядеть так:
const x = 10; const z = 10; add (x, z); // вернет 20
В этом случае функция всегда будет возвращать предсказуемый результат. Если функция не будет работать предсказуемо – это приведет к нежелательным побочным эффектам.
Еще один пример не функционального кода – классические циклы. Вспомним, как выглядит типичный цикл for в JavaScript:
var acc = 0; for (var i = 1; i <= 10; ++i) { acc += i; } console.log(acc); // выведет 55
Первое, на что нужно обратить внимание, – то, что цикл использует var i для подсчета шагов. В функциональном программировании использование такого цикла неприемлемо, так как это может привести к непредсказуемому поведению цикла.
Чтобы избежать побочных эффектов, в ФП для создания циклов используются рекурсивные функции.
function sumRange(start, end, acc) { if (start > end) { return acc; } else { return sumRange(start + 1, end, acc + start); } } console.log(sumRange(1, 10, 0)); // выведет 55
Такая конструкция позволяет использовать константы для определения начала, конца цикла и шага. В основе такого типа цикла лежит идея вызова функции внутри себя, или рекурсивного вызова. В примере выше функция sumRange() с заданными аргументами делает проверку условия, и в случае ложного результата вызывает саму себя с измененными аргументами.
Композиция функций
Композиция функций – это подход в функциональном программировании, который подразумевает вызов одних функций в качестве аргументов других, для создания сложных составных функций из более простых.
function addOne(x) { return x + 1; } function timesTwo(x) { return x * 2; } console.log(addOne(timesTwo(3))); // выведет 7 console.log(timesTwo(addOne(3))); // выведет 8
В примере выше мы описали две простые функции: addOne (прибавляет к аргументу единицу) и timesTwo (умножает аргумент на два). Техника компоновки позволяет нам вызывать две эти функции друг в друге в разном порядке. В результате, с разным логическим порядком вызова чистых функций и одинаковым значением аргумента мы получили более сложный функционал, который дает нам необходимый результат и делает это предсказуемо.
Польза функционального программирования
Функциональное программирование помогает сделать код более чистым, предсказуемым и легко читаемым. Использование принципов ФП помогает избавиться от лишних абстракций с непредсказуемым поведением, следовательно, сделать работу программы более предсказуемой и снизить количество возможных ошибок.
Недостатки функционального программирования
Функциональное программирование плохо подходит для алгоритмов, основанных на графах из-за сравнительно более медленной работы программы. ФП в целом плохо применимо в решениях, которые годами основывались на использовании императивного подхода.
Самое важное – в функциональных языках нет эффективного неупорядоченного словаря множества. В сравнении с хэш-таблицами, функциональные словари работают значительно хуже, а это может быть критично для некоторых приложений. Однако указанные недостатки можно отнести только к императивным языкам.
Функциональное программирование в языках
Так как функциональное программирование – это прежде всего подход к написанию кода, использовать его принципы можно в любом языке. Однако существуют языки, специально заточенные под функциональный подход. Первый и самый известный из них – Lisp. Он появился еще в 1958 году. Его автор – Джон Маккарти, информатик и автор термина «искусственный интеллект». Lisp по сей день популярен в среде проектировщиков ИИ.
Более современные функциональные языки, такие как Elm и Elixir, по данным GitHub и Stack Overflow постепенно и уверенно набирают популярность. Рост популярности JavaScript также привел к повышенному интересу к концепциям функционального программирования для применения в этом языке.