Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Каррирование JS: Функциональное программирование

Каррирование — очень интересная техника, позволяющая сильно упростить использование частичного применения на практике. Многие путают эти понятия, но, как вы увидите далее, они обозначают совершенно разные вещи.

В предыдущем уроке нам приходилось писать функцию-обертку для применения.

const getProgrammersSalaryByCountry =
  (country) => getAverageSalary('programmer', country);

const salary1 = getProgrammersSalaryByCountry('spain');
const salary2 = getProgrammersSalaryByCountry('russia');
const salary3 = getProgrammersSalaryByCountry('usa');

Eсли бы наша функция getAverageSalary была каррирована, то все было бы значительно проще. Каррирование — это процесс превращения функции от n аргументов в цепочку вложенных n-функций от одного аргумента. Соответственно, каррированная функция — это множество функций от одного аргумента.

Предположим, что у нас есть функция const sum = (a, b, c) => a + b + c, которая складывает три числа. Тогда ее каррированная версия будет выглядеть так: const sum2 = a => b => c => a + b + c, а использование таким: sum2(5)(10)(-2). То же самое, разложенное по функциям:

const sum2 = (a) => {
  return (b) => {
    return (c) => {
      return a + b + c;
    };
  };
};

Посмотрите внимательно на определение a => b => c => a + b + c. Эта запись очень краткая и одновременно очень емкая. Суммарное количество функций считается очень легко, оно равно сумме всех стрелок =>. Для лучшего понимания можно добавить скобки a => (b => (c => a + b + c)). Для каждого определения функции все, что находится справа от стрелки, является ее телом, каким бы сложным оно ни было. Поначалу такая запись может помочь понять, как друг в друга вложены функции, но со временем, когда вы привыкнете к обычной записи, она будет больше мешать.

С другой стороны, если нам нужно каррировать существующую функцию без ее реализации, то делается это так: const sum2 = a => b => c => originalSum(a, b, c). То есть создается цепочка вложенных функций, в которой количество вложений равно количеству аргументов исходной функции и в конце которой происходит вызов оригинальной функции.

Разберем происходящее по шагам.

const sum = (a) => (b) => (c) => a + b + c;
const sum1 = sum(10); // sum1 = b => c => 10 + b + c
const sum2 = sum1(3); // sum2 = c => 10 + 3 + c
const result = sum2(0); // result = 10 + 3 + 0
console.log(result); // => 13

То же самое происходит и при таком вызове: sum(10)(3)(0), разница только в том, что вызовы происходят без создания промежуточных констант.

И задача для самоконтроля. Сколько раз нужно вызвать цепочку функций const greeting = () => () => () => () => console.log('Hey!'), чтобы дойти до конца? Обязательно попробуйте прямо сейчас выполнить это задание на repl.it.

Теперь вернемся к нашей функции расчета зарплаты в ее каррированном виде. Представим, что теперь у нас в распоряжении две вложенных функции от одного аргумента: const getAverageSalary = job => country => /* body */:

const salary1 = getAverageSalary('programmer')('spain');
const salary2 = getAverageSalary('programmer')('russia');
const salary3 = getAverageSalary('programmer')('usa');

Попробуем частично применить:

const getProgrammersSalaryByCountry = getAverageSalary('programmer');

const salary1 = getProgrammersSalaryByCountry('spain');
const salary2 = getProgrammersSalaryByCountry('russia');
const salary3 = getProgrammersSalaryByCountry('usa');

Применение в реальной жизни

В функциональных языках, подобных haskell, вопросов об использовании каррированных функций просто не встает. Они там используются постоянно, начиная с самых азов. Это одна из причин, почему стоит учить подобные языки, даже если вы не будете писать на них программы. В императивных языках такое происходит значительно реже, но не в JS. JS по своей сути очень близок к функциональным языкам. Изначально он должен был быть написан на Scheme, языке из семейства Lisp, и функции в нем занимают центральное место. Каррирование используется в различных библиотеках и иногда является их ключевой "фичей", как, например, здесь: https://github.com/substantial/updeep.


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»