Примеры применения Ramda в JavaScript
Мыслим в парадигме функционального программирования с Ramda
ГлавнаяБлогРазработчикамМыслим в парадигме функционального программирования с Ramda
Разработчикам10 ноября 2022

Мыслим в парадигме функционального программирования с Ramda

Евгений ЖиленкоФронтенд-разработчик

Статья будет полезна тем, кто хочет приоткрыть для себя дверь в мир функционального программирования. В начале рассмотрим отличия ООП и ФП, затем как сочетать функции друг с другом и в конце применим знаменитые фишки функционального подхода.  

Давайте для начала разберемся с основными особенностями парадигмы функционального программирования, для простоты восприятия  оттолкнемся от объектно-ориентированного подхода.

Отличия объектно-ориентированного и функционального подходов

Что такое ООП?

  1. Работаем с объектами, то есть классами и экземплярами этих классов;
  2. Пишем в императивном стиле;
  3. Классы тесно связаны между собой;
  4. Данные и методы по работе с классом лежат в самом классе. Эти данные должны быть инкапсулированы и доступ к ним должен осуществляться через геттеры/сеттеры и методы этого класса;
  5. Данные являются изменяемыми. Мы можем их изменить в любой момент работы нашего кода.
     

Что такое ФП?

  1. Работаем с функциями;
  2. Пишем в декларативном стиле;
  3. Каждая функция - самостоятельная единица;
  4. Используем чистые функции. Они не производят побочных эффектов, то есть не изменяют входные параметры, ничего не выводят, ничего не присваивают внешним переменным, не вызывают нечистые функции, не обращаются к внешним API, работают детерминировано; 
  5. Данные являются иммутабельными.


Основное отличие в том, что в ФП важно не то, как мы хотим что-то получить, а что мы хотим получить.

С чего начать, чтобы мыслить в парадигме функционального программирования?

Сперва попробуем вместо циклов использовать итерационные функции. Применим те, которые дает нам Ramda :
Изображение статьи
 Для операций с массивами, в Ramda имеются и другие:
Изображение статьи Изображение статьи Изображение статьи Изображение статьи

Начинаем соединять функции

В предыдущих примерах мы применяли find для нахождения первого нечетного числа в массиве. Если бы у нас была задача найти первое четное число, мы создали бы isEven и использовали ее. Но мы знаем, что любое четное число не является нечетным. Здесь мы переиспользуем isOdd. В Ramda есть метод complement, который является функцией высшего порядка. Он принимает в себя isOdd. На выходе получится новая функция, возвращающая true, когда наша оригинальная isOdd возвращает false, и наоборот:

Изображение статьи

Она делает то же самое, что оператор !. Давайте запишем эту функции в переменную с соответствующим именем, чтобы мы могли переиспользовать её:
Изображение статьи
Существуют аналоги операторов && и ||  — функции both / either соответственно:
Изображение статьи В Ramda также присутствуют allPass и anyPass :
Изображение статьи Изображение статьи

Допустим, нужно обработать данные друг за другом, конвейером. Например, перемножить два числа, к произведению прибавить единицу, а результат возвести в квадрат:
Изображение статьи

Заметьте, что каждая последующая функция применяется к результату предыдущей в конвейере. Ramda предоставляет инструмент pipe, где принимает список из нескольких функций, затем возвращает новую, которая ожидает на вход такое же количество параметров, что и первая в списке. После передает результат работы первой во вторую, и так далее. Результат работы заключительной функции в списке будет являться  результатом прохождения всего конвейера. Есть ограничение, каждая функция после первой должна ожидать только один аргумент.
Изображение статьи Все функции в Ramda автоматически каррированы. Это означает, что вы можете вызывать функцию с меньшим количеством параметров, чем она требует, и она вернет «частично примененную».  Функции будут продолжать возвращать новые пока все параметры не будут переданы. И когда будет передан последний параметр произойдет вызов последней функции, который вернет финальный результат.
Изображение статьи
В Ramda так же есть функция curry, которая позволяет каррировать любую другую.


Изображение статьи Каррированые функции позволяют нам делать две важные вещи в функциональном программировании — специализацию и композицию.

Специализация происходит от возможности каррированых функций быть «частично примененными». Мы можем сохранить эти частично примененные методы в переменные и использовать позже.
Изображение статьи
Другая важная концепция — это композиция функций. Мы можем комбинировать так, чтобы получить новую функцию. Тут мы создали новую, которая возвращает результат работы обеих double и increment. Изображение статьи

Мы сделали композицию. Выглядит неплохо, но что будет если мы захотим сделать композицию из более чем двух?
Изображение статьи Теперь это выглядит плохо. Но мы уже знаем pipe и с ее помощью можем записать в более человеко-читаемой манере.
Изображение статьи Это же можно применить и к каррированым. Возьмем для примера встроенные функции Ramda, хотя это будет справедливо для любой из каррированых.
Изображение статьи

В данном примере multiply и add принимают 2 параметра, но мы передали только один. Каждая из них вернула новую, которая ждет последнего параметра. Таким образом, когда мы вызываем mathPipe со значением 10, первая, частично примененная, возвращает нам 40 и затем это значение передается во вторую, частично примененную и в итоге мы получим 42. 


 На простых примерах мы рассмотрели, как функциональное программирование помогает писать  более чистый код, который можно переиспользовать.

1.1к
100

Другие статьи

Ко всем статьям
Фоновое изображение: четверть круга закрыват часть круга

Интересные статьи и кейсы
от Work Solutions

Нажимая кнопку «Подписаться», я даю согласие на обработку персональных данных

Спасибо за подписку!

Фоновое изображение: верхний полукруг