Каррирование и частичное применение
Каррирование
Преобразование функции с несколькими аргументами в цепочку функций с одним:
// Обычная
const add = (a, b) => a + b;
add(1, 2); // 3
// Каррированная
const addCurried = (a) => (b) => a + b;
addCurried(1)(2); // 3
Зачем
Создание специализированных функций:
const multiply = (a) => (b) => a * b;
const double = multiply(2);
const triple = multiply(3);
double(5); // 10
triple(5); // 15
Каррирование вручную
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return (...next) => curried.apply(this, [...args, ...next]);
};
}
const add3 = (a, b, c) => a + b + c;
const curriedAdd = curry(add3);
curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6
curriedAdd(1)(2, 3); // 6
curriedAdd(1, 2, 3); // 6
fn.length — число параметров до первого со значением по умолчанию.
Частичное применение
Фиксирует часть аргументов, возвращает функцию для остальных:
function partial(fn, ...fixedArgs) {
return (...restArgs) => fn(...fixedArgs, ...restArgs);
}
const greet = (greeting, name) => `${greeting}, ${name}`;
const sayHello = partial(greet, 'Привет');
sayHello('Алиса'); // 'Привет, Алиса'
sayHello('Боб'); // 'Привет, Боб'
Bind как частичное
bind может фиксировать аргументы:
const greet = (greeting, name) => `${greeting}, ${name}`;
const sayHi = greet.bind(null, 'Привет');
sayHi('Алиса'); // 'Привет, Алиса'
Каррирование vs частичное
- Каррирование — превращает
f(a, b)вf(a)(b) - Частичное применение — фиксирует часть аргументов
Частичное — более общее. Каррирование — частный случай.
Практика: логгер
const log = (level) => (message) => {
console.log(`[${level}] ${message}`);
};
const info = log('INFO');
const error = log('ERROR');
info('Запуск'); // [INFO] Запуск
error('Сбой'); // [ERROR] Сбой
Практика: фильтры
const filterBy = (key) => (value) => (items) =>
items.filter((item) => item[key] === value);
const byRole = filterBy('role');
const admins = byRole('admin');
admins(users);
Практика: API-клиент
const apiRequest = (baseUrl) => (endpoint) => (options = {}) =>
fetch(`${baseUrl}${endpoint}`, options).then((r) => r.json());
const api = apiRequest('https://api.example.com');
const getUsers = api('/users');
const getPosts = api('/posts');
getUsers();
getPosts();
Практика: валидация
const minLength = (min) => (value) =>
value.length >= min ? null : `Минимум ${min} символов`;
const min8 = minLength(8);
const min3 = minLength(3);
min8('abc'); // 'Минимум 8 символов'
min8('abcdefgh'); // null
Композиция
const compose = (...fns) => (x) =>
fns.reduceRight((acc, fn) => fn(acc), x);
const addOne = (x) => x + 1;
const double = (x) => x * 2;
const addThenDouble = compose(double, addOne);
addThenDouble(3); // 8
Когда использовать
- Много повторяющихся конфигураций
- Создание специализированных функций из общих
- Функциональный стиль
- Пайплайны обработки
Не переусложняй: часто обычная функция с параметрами понятнее.
Итоги
- Каррирование — цепочка функций с одним аргументом
- Частичное — фиксация части аргументов
bind— простой способ частичного- Полезно для логгеров, фильтров, API
- Не везде уместно