Частичное применение и каррирование
Замыкания — основа для partial application и currying. Из функции с N аргументами получаем цепочку функций с одним аргументом, фиксируя часть аргументов в замыкании.
Идея
автор: функция — это абстракция, и уровень абстракции можно менять. Из конкретной функции (Math.log — натуральный) можно сделать абстрактную (log(n, base) = log(n) / log(base)). А потом частично применить базу и получить новую специализированную функцию.
Замыкание как механизм
// Из двух аргументов делаем функцию одного через вложенные лямбды
const createLog = (base) => (n) => Math.log(n) / Math.log(base);
const lg = createLog(10);
const ln = createLog(Math.E);
Если функция возвращена из контекста другой функции, то она будет видеть контекст верхней функции — это и называется замыкание. Мы замкнули функцию на контекст.
bind для partial
function log(base, n) { return Math.log(n) / Math.log(base); }
const lg = log.bind(null, 10); // 10 → base, n остался
lg(1000); // log(10, 1000)
bind закрепляет this и часть аргументов слева. Возврат — новая функция с меньшей арностью.
Своя partial без bind
const partial = (fn, ...args) =>
(...rest) => fn(...args, ...rest);
const sum = (a, b, c) => a + b + c;
const f1 = partial(sum, 10); // a = 10
const f2 = partial(f1, 5); // b = 5
f2(3); // sum(10, 5, 3) = 18
Определение
Частичное применение — это когда мы часть аргументов уже передали, применили, а какой-то остаток ещё торчит, и его нужно последовательно передавать.
Мы можем часть аргументов передать, а потом саму эту функцию с уже попавшими в неё аргументами передать по ссылке в другую часть программы, и никто не сможет выколупать те аргументы. — инкапсуляция через замыкание.
Curry: каждый аргумент по отдельности
const curry = (fn) => {
return function curried(...args) {
if (args.length >= fn.length) return fn.apply(this, args);
return (...rest) => curried.apply(this, args.concat(rest));
};
};
const add = curry((a, b, c) => a + b + c);
add(1)(2)(3); // 6
add(1, 2)(3); // 6
add(1)(2, 3); // 6
Где это нужно
- Создание специализированных утилит из общих
- Унификация арности под pipe/compose (все функции принимают 1 аргумент)
- Логгеры, валидаторы, обработчики событий с предзаполненной частью
- Замена громоздкого DI на цепочку partial
Тонкости
- Цепочка из 10 вложенных лямбд для функции от 10 аргументов — теоретически возможно, но неудобно
- bind теряет .name и не оптимально работает с this в стрелочных функциях
- В V8 каждая partial-функция = отдельный closure scope; держи руку на пульсе, если их создаются миллионы
Связанные темы
- Каррирование
- Partial Application
- Замыкания (Closures)
- Композиция функций (pipe, compose)
- Композиция функций (pipe, compose)
Ресурсы
- Лекция а: ND8KQ5xjk7o
- Архивная часть 3: OBTYfpCRABA