Partial Application
Частичное применение — закрепление части аргументов функции с получением новой функции от оставшихся. В отличие от каррирования, partial — это один шаг, не цепочка.
Что это / Зачем
- Дана
f(a, b, c), фиксируемa = 1→ получаемg(b, c) = f(1, b, c) - Способ из конкретной функции породить более конкретную
- Базовый кирпич для каррирования и декораторов
- В стандарте есть
Function.prototype.bind— это и есть partial
Примеры
Через bind
const log = (n, base) => Math.log(n) / Math.log(base);
const lg = log.bind(null, 10); // log по основанию 10
const ln = log.bind(null, Math.E); // натуральный
lg(100); // 2
Своя реализация
const partial = (fn, ...preset) =>
(...later) => fn(...preset, ...later);
const sum4 = (a, b, c, d) => a + b + c + d;
const f1 = partial(sum4, 1); // a = 1
const f12 = partial(f1, 2); // a = 1, b = 2
f12(3, 4); // 10
Partial vs Currying
| Partial | Currying |
|---|---|
| Один шаг закрепления | Цепочка из n функций |
| Возвращает функцию любой арности | Каждая возвращает функцию от 1 (или больше) |
bind — встроенная реализация |
Нужна своя реализация |
| Удобен ситуативно | Удобен системно |
// partial: один шаг, остаток сразу
const add5 = partial(add, 5);
add5(3, 7); // a=5, b=3, c=7
// curry: цепочка шагов
const cAdd = curry(add);
cAdd(5)(3)(7); // три отдельных вызова
Ключевые правила
bindзакрепляет аргументы слева направо- Первый аргумент
bind—this, дальше — аргументы - Лямбды (arrow functions) не привязывают
thisчерезbind, но аргументы закрепляются - Можно делать последовательно:
partial(partial(f, 1), 2)
Подводные камни
bind(null, ...)оставляетthis = null— может ломать методы- Стрелочная функция не знает про
thisизbind - В цепочке
partialкаждый вызов создаёт новую функцию (память)
🎓 Источник: Частичное применение и каррирование в JavaScript
- 📅 2018-10-08 · YouTube · ID:
ND8KQ5xjk7o - Тезисы:
- Из конкретной функции (Math.log) делаем абстрактную (log с основанием), потом снова конкретную (lg, ln) — это функциональное наследование
bind— встроенный partial, закрепляет слева, первый аргумент —this(nullесли не нужен)- Лямбды не работают с
bindдляthis, но для аргументов работают - Своя реализация через rest + spread проще и не требует
null
- Цитата: «Это получилось некоторого рода наследование, функциональное наследование. То есть мы сначала от более конкретной функции перешли на более абстрактную, потом унаследовали от нее две других и вычислили.»