Статья будет полезна тем, кто хочет приоткрыть для себя дверь в мир функционального программирования. В начале рассмотрим отличия ООП и ФП, затем как сочетать функции друг с другом и в конце применим знаменитые фишки функционального подхода.
Давайте для начала разберемся с основными особенностями парадигмы функционального программирования, для простоты восприятия оттолкнемся от объектно ориентированного подхода.
Отличия объектно ориентированного и функционального подходов
Что такое ООП?
- Работаем с объектами, то есть классами и экземплярами этих классов;
- Пишем в императивном стиле;
- Классы тесно связаны между собой;
- Данные и методы по работе с классом лежат в самом классе. Эти данные должны быть инкапсулированы и доступ к ним должен осуществляться через геттеры/сеттеры и методы этого класса;
- Данные являются изменяемыми. Мы можем их изменить в любой момент работы нашего кода.
Что такое ФП?
- Работаем с функциями;
- Пишем в декларативном стиле;
- Каждая функция - самостоятельная единица;
- Используем чистые функции. Они не производят побочных эффектов, то есть не изменяют входные параметры,ничего не выводят, ничего не присваивают внешним переменным, не вызывают нечистые функции, не обращаются к внешним API, работают детерминировано;
- Данные являются иммутабельными.
Основное отличие в том, что в ФП важно не то, как мы хотим что-то получить, а что мы хотим получить.
С чего начать, чтобы мыслить в парадигме функционального программирования?
Сперва попробуем вместо циклов использовать итерационные функции. Применим функции, которые дает нам Ramda :
Для операций с массивами, в Ramda имеются и другие функции:
Начинаем соединять функции
В предыдущих примерах мы применяли функцию find для нахождения первого нечетного числа в массиве. Если бы у нас была задача найти первое четное число, мы создали бы функцию isEven и использовали ее. Но мы знаем, что любое четное число не является нечетным. Здесь мы переиспользуем нашу старую функцию isOdd. В Ramda есть функция complement, которая является функцией высшего порядка. Она принимает в себя функцию, в нашем примере isOdd. На выходе получится новая функция, возвращающая true, когда наша оригинальная функция isOdd возвращает false, и наоборот:
Она делает тоже самое, что оператор !. Давайте запишем эту функции в переменную с соответствующим именем, чтобы мы могли переиспользовать её:
Существуют аналоги операторов && и || — функции both / either соответственно:
Допустим, нужно обработать данные друг за другом, конвейером. Например, перемножить два числа, к произведению прибавить единицу, а результат возвести в квадрат:
Заметьте, что каждая последующая функция применяется к результату предыдущей функции в конвейере. Ramda предоставляет инструмент pipe. Эта функция принимает список из нескольких функций, затем возвращает новую, которая ожидает на вход такое же количество параметров, что и первая функция в списке, передает результат работы первой функции во вторую, и так далее. Результат работы заключительной функции в списке будет являться результатом прохождения всего конвейера. Есть ограничение, каждая функция после первой должна ожидать только один аргумент.
В Ramda так же есть функция curry, которая позволяет каррировать любую другую функцию.
Специализация происходит от возможности каррированых функций быть «частично примененными». Мы можем сохранить эти частично примененные функции в переменные и использовать позже.
Другая важная концепция — это композиция функций. Мы можем комбинировать функции, чтобы получить новую функцию. Тут мы создали новую функцию, которая возвращает результат работы обеих функций double и increment.
На простых примерах мы рассмотрели, как функциональное программирование помогает писать более чистый код, который можно переиспользовать.