← Назад к вопросам
Что такое каррирование?
2.0 Middle🔥 151 комментариев
#JavaScript Core#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое каррирование (Currying)
Каррирование (currying) — это функциональный паттерн, где функция, принимающая несколько аргументов, преобразуется в последовательность функций, каждая из которых принимает по одному аргументу. Это мощный инструмент для создания переиспользуемого кода.
Базовое объяснение
Обычная функция:
// Классическая функция с несколькими аргументами
function add(a, b, c) {
return a + b + c;
}
add(1, 2, 3); // 6
То же самое с каррированием:
// Каррированная функция
function curriedAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
curriedAdd(1)(2)(3); // 6
// Или с arrow functions (более читаемо)
const curriedAdd = a => b => c => a + b + c;
curriedAdd(1)(2)(3); // 6
Преимущество: Частичное применение (Partial Application)
Каррирование позволяет "зафиксировать" некоторые аргументы:
const curriedAdd = a => b => c => a + b + c;
// Создаём новую функцию с фиксированным первым аргументом
const addTen = curriedAdd(10);
console.log(addTen(5)(3)); // 10 + 5 + 3 = 18
// Или с двумя фиксированными аргументами
const addTenAndFive = curriedAdd(10)(5);
console.log(addTenAndFive(3)); // 10 + 5 + 3 = 18
Практические примеры
Пример 1: Валидация данных
// Каррированная функция валидации
const validator = (type) => (minValue) => (value) => {
if (type === 'number' && typeof value !== 'number') {
return false;
}
if (type === 'string' && typeof value !== 'string') {
return false;
}
if (type === 'number' && value < minValue) {
return false;
}
return true;
};
// Создаём специализированные валидаторы
const isValidAge = validator('number')(0);
const isValidAdult = validator('number')(18);
const isValidName = validator('string')(0);
console.log(isValidAge(25)); // true
console.log(isValidAdult(16)); // false (меньше 18)
console.log(isValidName('John')); // true
console.log(isValidName(123)); // false (не строка)
Пример 2: Создание запросов к API
// Каррированная функция для HTTP запросов
const makeRequest = (method) => (url) => (data) => {
return fetch(url, {
method: method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}).then(res => res.json());
};
// Создаём методы
const POST = makeRequest('POST');
const GET = makeRequest('GET');
// Создаём специфичные запросы
const createUser = POST('https://api.example.com/users');
const createPost = POST('https://api.example.com/posts');
// Используем
createUser({ name: 'John', email: 'john@example.com' });
createPost({ title: 'Hello', content: '...' });
Пример 3: Логирование
// Логгер с префиксом
const logger = (level) => (module) => (message) => {
console.log(`[${level}] [${module}] ${message}`);
};
const error = logger('ERROR');
const warn = logger('WARN');
const info = logger('INFO');
// Создаём логгеры для разных модулей
const authError = error('Auth');
const dbWarn = warn('Database');
const apiInfo = info('API');
// Используем
authError('Failed to authenticate'); // [ERROR] [Auth] Failed to authenticate
dbWarn('Slow query detected'); // [WARN] [Database] Slow query detected
apiInfo('Request received'); // [INFO] [API] Request received
Утилита для автоматического каррирования
// Автоматически преобразует обычную функцию в каррированную
function curry(fn) {
return function curried(...args) {
// Если передано достаточно аргументов, выполняем функцию
if (args.length >= fn.length) {
return fn.apply(this, args);
}
// Иначе возвращаем функцию, которая ждёт остальные аргументы
return (...nextArgs) => curried(...args, ...nextArgs);
};
}
// Обычная функция
function add(a, b, c) {
return a + b + c;
}
// Преобразуем в каррированную
const curriedAdd = curry(add);
// Теперь можно использовать обеими способами
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6 (можно передать несколько аргументов за раз)
console.log(curriedAdd(1, 2, 3)); // 6 (обычный вызов тоже работает)
const addFive = curriedAdd(5);
console.log(addFive(2, 3)); // 10
Пример 4: Композиция функций (Function Composition)
Каррирование хорошо работает с композицией:
// Каррированные функции
const multiply = (a) => (b) => a * b;
const add = (a) => (b) => a + b;
const subtract = (a) => (b) => a - b;
// Создаём специализированные функции
const multiplyByTwo = multiply(2);
const addTen = add(10);
const subtractFive = subtract(5);
// Комбинируем их
const result = addTen(multiplyByTwo(5)); // add(10)(multiply(2)(5)) = 10 + 10 = 20
console.log(result); // 20
Пример 5: Redux-style actions
// Каррированная функция для создания reducer
const createReducer = (initialState) => (reducer) => {
return (state = initialState, action) => {
return reducer(state)(action);
};
};
// Или более реальный пример: middleware
const middleware = (store) => (next) => (action) => {
console.log('Before:', action);
const result = next(action);
console.log('After:', store.getState());
return result;
};
// Redux использует это под капотом!
applyMiddleware(middleware);
Куда каррирование НЕ нужно
// ❌ Плохо: каррирование усложняет код
const simpleAdd = (a, b) => a + b;
// ✅ Хорошо: просто использовать обычную функцию
const result = simpleAdd(5, 3);
// ❌ Плохо: каррирование для одной функции
const curriedAdd = a => b => a + b;
// Она не переиспользуется, зачем усложнять?
Разница между Currying и Partial Application
// Currying: разбить на одноаргументные функции
const curriedSum = a => b => c => a + b + c;
curriedSum(1)(2)(3);
// Partial Application: зафиксировать некоторые аргументы
const partial = (fn, ...args) => (...nextArgs) => fn(...args, ...nextArgs);
const sum = (a, b, c) => a + b + c;
const addTen = partial(sum, 10);
addTen(5, 3); // 18
// Это разные подходы, но часто используются вместе
Производительность
// ❌ Много вложенных функций = медленнее
const curriedAdd = a => b => c => a + b + c;
const result = curriedAdd(1)(2)(3); // создаёт 3 функции
// ✅ Обычная функция быстрее
const add = (a, b, c) => a + b + c;
const result = add(1, 2, 3);
Разница незначительна в большинстве случаев, но для критичного по производительности кода стоит помнить.
Ключевые выводы
- Каррирование преобразует функцию с N аргументами в N функций с одним аргументом
- Позволяет частичное применение аргументов
- Удобно для создания переиспользуемых функций
- Хорошо для функциональной композиции
- Может усложнить читаемость если переусложнить
- Используй только когда есть реальная выгода
- Redux middleware и другие библиотеки используют каррирование