Ответ
Каррирование — это техника преобразования функции с несколькими аргументами в последовательность функций, каждая из которых принимает по одному аргументу. Это позволяет создавать специализированные функции через частичное применение.
Базовый пример:
// Обычная функция
const add = (a, b, c) => a + b + c;
// Каррированная версия
const curriedAdd = a => b => c => a + b + c;
console.log(add(1, 2, 3)); // 6
console.log(curriedAdd(1)(2)(3)); // 6
Практическое применение в Node.js:
// Каррирование для создания middleware с конфигурацией
const createLogger = (serviceName) => (logLevel) => (message) => {
console.log(`[${serviceName}] [${logLevel.toUpperCase()}] ${message}`);
};
const authServiceLogger = createLogger('AuthService');
const authErrorLogger = authServiceLogger('error');
const authInfoLogger = authServiceLogger('info');
// Использование специализированных логгеров
authErrorLogger('Invalid token provided'); // [AuthService] [ERROR] Invalid token provided
authInfoLogger('User login successful'); // [AuthService] [INFO] User login successful
// Каррирование для работы с базами данных
const createQuery = (dbClient) => (tableName) => (conditions) => {
return dbClient.query(`SELECT * FROM ${tableName} WHERE ?`, [conditions]);
};
const queryUsers = createQuery(dbClient)('users');
const activeUsersQuery = queryUsers({ status: 'active' });
// activeUsersQuery - готовая к выполнению функция с предустановленными параметрами
Утилита для каррирования:
// Универсальная функция каррирования
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
// Использование
const multiply = (a, b, c) => a * b * c;
const curriedMultiply = curry(multiply);
const double = curriedMultiply(2);
const doubleAndTriple = double(3);
console.log(doubleAndTriple(4)); // 24 (2 * 3 * 4)
Преимущества каррирования в Node.js:
- Переиспользование кода: Создание специализированных функций из общих
- Композиция: Упрощает создание цепочек вызовов и композицию функций
- Отложенное выполнение: Можно подготовить функцию с частью аргументов и выполнить позже
- Чистые функции: Каррированные функции легче тестировать и отлаживать
Ответ 18+ 🔞
Ну ты представляешь, эта ваша функциональщина с её каррированием — выглядит как какая-то магия для непосвящённых, но на деле-то всё просто, как три копейки. Сейчас разжуём.
Вот смотри, обычная функция — это как бутерброд: взял сразу все ингредиенты, собрал и съел. А каррированная — это как если бы тебе сначала дали хлеб, потом, через пять минут, масло, а потом ещё через десять — колбасу. По одному кусочку за раз. Выглядит, конечно, как издевательство, но иногда так удобнее.
Базовый пример, чтобы въехать:
// Нормальная, человеческая функция
const add = (a, b, c) => a + b + c;
// А это её каррированный уродец
const curriedAdd = a => b => c => a + b + c;
console.log(add(1, 2, 3)); // 6 — быстро и понятно
console.log(curriedAdd(1)(2)(3)); // 6 — тот же результат, но вызовов как у многоэтажки
Суть в чём? Вместо того чтобы ждать все три аргумента сразу, функция curriedAdd принимает первый, возвращает новую функцию, которая ждёт второй, и так далее. Поначалу кажется, что это пиздопроебина полная, зачем так усложнять? А потом находишь кейсы, где это реально спасает жопу.
Вот смотри, как это в реальной жизни применить, например, в логгере:
// Каррирование для создания middleware с конфигурацией
const createLogger = (serviceName) => (logLevel) => (message) => {
console.log(`[${serviceName}] [${logLevel.toUpperCase()}] ${message}`);
};
// Сперва создаём логгер для сервиса авторизации
const authServiceLogger = createLogger('AuthService');
// Потом из него делаем специализированный логгер для ошибок
const authErrorLogger = authServiceLogger('error');
// И для информационных сообщений
const authInfoLogger = authServiceLogger('info');
// Используем готовые, заточенные под конкретную задачу функции
authErrorLogger('Invalid token provided'); // [AuthService] [ERROR] Invalid token provided
authInfoLogger('User login successful'); // [AuthService] [INFO] User login successful
Чувствуешь мощь? Один раз описал общую структуру, а потом на её основе наштамповал кучу готовых, пристрелянных функций. Не нужно каждый раз таскать с собой название сервиса и уровень логирования — всё уже внутри. Удобно, блядь.
Или вот работа с базой данных — классика жанра:
const createQuery = (dbClient) => (tableName) => (conditions) => {
return dbClient.query(`SELECT * FROM ${tableName} WHERE ?`, [conditions]);
};
// Цепочка: сначала клиент БД, потом таблица, потом условия
const queryUsers = createQuery(dbClient)('users');
const activeUsersQuery = queryUsers({ status: 'active' });
// activeUsersQuery — это уже готовая, заряженная функция, которую можно выполнить когда угодно
А если лень каждый раз писать эти стрелочки-закорючки, есть универсальная утилитка:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
// Использовать — проще пареной репы
const multiply = (a, b, c) => a * b * c;
const curriedMultiply = curry(multiply);
const double = curriedMultiply(2); // Частично применили первый аргумент
const doubleAndTriple = double(3); // Применили второй
console.log(doubleAndTriple(4)); // 24 (2 * 3 * 4) — и вот финальный расчёт
Так зачем весь этот геморрой, спросишь? А вот зачем, чувак:
- Переиспользование кода до охуища. Сделал один раз общую функцию — как из конструктора налепил кучу специфичных. Экономия времени и нервов.
- Композиция. Такие функции, как кубики Лего, цепляются друг за друга. Строить сложную логику из простых кусочков становится в разы проще.
- Отложенное выполнение. Можешь подготовить функцию наполовину, а остальные аргументы скормить ей позже, когда они появятся. Как полуфабрикат в холодильнике.
- Чистые функции. Их проще тестировать, отлаживать и просто понимать, что они делают. Никаких сюрпризов из-за внешнего состояния.
В общем, инструмент хоть и специфичный, но в арсенале иметь стоит. Особенно когда проект растёт и нужно поддерживать в нём хоть какой-то порядок, а не бардак ебаный.