Каррирование - это способ конструирования функций, позволяющий частичное применение аргументов функции. Т.е. мы можем передать все аргументы, ожидаемые функцией и получить результат, или же передать часть этих аргументов и получить обратно функцию, которая ожидает остальные аргументы.
// Обычная функция приветствия:
var greet = function(greeting, name) {
console.log(greeting + ", " + name);
};
greet("Hello", "Heidi"); // "Hello, Heidi"
// Каррируем:
var greetCurried = function(greeting) {
return function(name) {
console.log(greeting + ", " + name);
};
};
var greetHello = greetCurried("Hello");
greetHello("Heidi"); // "Hello, Heidi"
greetHello("Eddie"); // "Hello, Eddie"
greetCurried("Hi there")("Howard"); // "Hi there, Howard"
// Каррируем глубже:
var greetDeeplyCurried = function(greeting) {
return function(separator) {
return function(emphasis) {
return function(name) {
console.log(greeting + separator + name + emphasis);
};
};
};
};
var greetAwkwardly = greetDeeplyCurried("Hello")("...")("?");
greetAwkwardly("Heidi"); // "Hello...Heidi?"
greetAwkwardly("Eddie"); // "Hello...Eddie?"
var sayHello = greetDeeplyCurried("Hello")(", ");
sayHello(".")("Heidi"); // "Hello, Heidi."
sayHello(".")("Eddie"); // "Hello, Eddie."
При создании каррированных функций нам надо сохранять вложенность возвращаемых функций и вызывать их с помощью новых функций, требующих многочисленные наборы скобок, в каждом из которых содержится свой изолированный аргумент. Это может стать запутанным.
Одним из путей решения этой проблемы является создание быстрой и грязной каррирующей функции, которая будет принимать имя существующей функции, написанной без всех вложенных возвращений. Каррирующая функция должна вытащить список аргументов для этой функции и использовать их для возврата каррированной версии оригинальной функции.
// ES5:
function curryIt(uncurriedFn) {
var params = [].slice.call(arguments, 1);
return function() {
return uncurriedFn.apply(this, params.concat(
[].slice.call(arguments, 0)
));
};
};
// ES6:
function curryIt(uncurriedFn, ...params) {
return (...args) => uncurriedFn.apply(this, [...params, ...args]);
}
// ES6 alt:
const curryIt = (uncurriedFn, ...params) => (...args) => uncurriedFn.apply(this, [...params, ...args]);
var greeter = function(greeting, separator, emphasis, name) {
console.log(greeting + separator + name + emphasis);
};
var greetHello = curryIt(greeter, "Hello", ", ", ".");
greetHello("Heidi"); // "Hello, Heidi."
greetHello("Eddie"); // "Hello, Eddie."
// Add numbers example:
function add(...args) {
return (args.length > 0) ? args.reduce((a, b) => a + b) : 0;
}
function curry(func) {
return function(...nextArgs) {
// run function
if (nextArgs.length === 0) return func.apply(this, [...nextArgs]);
// else return a partially applied function
return curry(func.bind.apply(func, [this, ...nextArgs]));
};
}
const sum = curry(add);
console.log(sum(), 0);
console.log(sum(3)(2)(5)(8)(3)(), 21);
const sum10 = sum(2)(4)(4); // ==> Function
console.log(sum10(), 10); // ==> 10
const sum13 = sum10(3); // ==> Function
console.log(sum13(), 13); // ==> 13
console.log(sum13(-4)(), 9); // ==> 9