JS: Функциональное программирование
Теория: Частичное применение
Частичное применение функций — техника, основанная на возможности возвращать функции из других функций. Допустим, у нас есть функция sum, суммирующая три числа.
Частичное применение позволяет на основе старой функции создать новую, которая "частично применена". Для начала вспомним, что такое применение функции. Математики никогда не говорят, что функция вызывается с некоторыми аргументами — вместо этого они говорят, что функция была применена к этим аргументам. В примере выше функция sum была применена к трем аргументам 4, 3 и 1. Такое применение можно назвать полным, то есть в функцию было передано столько аргументов сколько и ожидалось. Здесь возникает вопрос, а можно было по-другому? Да, можно.
Частичное применение, техника которую проще всего объяснить в отрыве от языков программирования, на языке близком к математике (для простоты назову его Ha). Представьте, что наша функция имеет вот такое определение:
А ее вызов выглядит так:
Ha — необычный язык. Если внутри него вызвать функцию с неполным набором параметров, то в отличие от js он не вызовет саму функцию (или не упадет с ошибкой как во многих других языках). Он вернет новую функцию, которая "частично применена".
Другими словами, такой вызов создает новую функцию, которая работает точно так же, как и исходная, но, при этом, часть ее аргументов как будто уже подставлены. То есть наш вызов sum2 1 1 в действительности приведет к вызову sum 4 1 1. Этот трюк работает с любым количеством аргументов. Посмотрите на частичное применение двух аргументов:
Арифметика аргументов очень простая. Если исходная функция принимала 3 параметра, то после частичного применения одного параметра, новая функция будет принимать на вход два параметра (2 + 1 = 3), если частично применились два параметра, то новая функция принимает на вход 1 параметр. А можно ли частично применить три параметра для функции, которая принимает на вход 3 параметра? Конечно нет, ведь это будет обычный вызов функции со всеми параметрами. Довольно несложно догадаться как выглядело бы определение функций sum2 и sum3 если бы мы их описали явно:
Ha настолько необычный язык, что позволяет частично применять то, что уже было частично применено:
В js эту возможность можно реализовать, например, с помощью дополнительной функции:
Функция partialApply принимает на вход исходную функцию и параметры которые нужно применить, а возвращает новую, частично примененную функцию.
Вот как может выглядеть реализация partialApply для функций от трех аргументов (таких как sum):
Немного примеров из реальности. Предположим, что мы пишем программу, вычисляющую зарплату людей разных специальностей по всему миру. Для этого используется функция getAverageSalary.
Первым параметром функция принимает на вход название профессии, вторым — страну, на выходе — годовая зарплата. Все довольно примитивно, но часть данных дублируется. Вычисляется зарплата программистов в разных странах, что приводит к постоянному вызову функции с первым параметром programmer. В этом нет ничего криминального, но существует способ устранить подобное повторение. Убрать с помощью введения дополнительной константы.
Название профессии больше не повторяется, но нам по-прежнему приходится подставлять его в каждый вывод. Здесь на помощь приходит так называемое частичное применение функции.
Принцип действия частичного применения функции основан на генерации (в нашем случае с помощью функции partialApply) новой функции, оборачивающей старую, причем так, что она принимает на вход меньшее количество аргументов. Для нашей задачи мы применили один аргумент, но это не обязательно. Применять можно любое число аргументов исходной функции (но, естественно, не все, ведь в таком случае мы получим обычный вызов функции). И хотя пример выше выглядит слегка искусственным, в реальной жизни этот прием применяется часто. Причем нередко частично применяется больше одного аргумента за раз.
Тело функции getProgrammersSalaryByCountry выглядит предсказуемо. Внутри вызывается исходная функция с подставленным в аргумент значением.
А partialApply для функций от двух аргументов реализуется способом очень похожим на реализацию flip.
То есть функция partialApply возвращает (генерирует) функцию, которая внутри себя замыкает два параметра: fn и arg1.
Частичное применение для JS — практически как воздух для живых организмов. Из-за асинхронной природы JS часто бывают ситуации, когда одни параметры функции задаются в одном месте, а другие в другом, так как они становятся доступны только после выполнения асинхронной операции.

