← Назад к вопросам

Что такое каррирование?

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 и другие библиотеки используют каррирование
Что такое каррирование? | PrepBro