How to curry a function across an unknown number of parameters
Using a functional approach, you can create a function that "curries" arguments for another function. You will need a way to tell the function to return the value, so in this case, calling the function without passing any arguments will return the result:
function curry(fn, ...values) {
return (...next) => (next.length) ? curry(fn, ...values, ...next) : fn(...values);
}
The cool thing about this function is that you can pass multiple arguments and/or keep invoking the function (1)(2, 3, 4)(5)
.
Here's a couple of examples:
function curry(fn, ...values) {
return (...next) => (next.length) ? curry(fn, ...values, ...next) : fn(...values);
}
function multiplyDivide(...args) {
return args.reduce((total, next, i) => (i % 2) ? (total / next) : (total * next), args.shift());
}
let x = curry(multiplyDivide)(2)(3, 4)(6)();
console.log(x);
let y = curry(multiplyDivide)(5, 4, 2)(3);
y = y(3, 5)(1)();
console.log(y);
Of course, this example does hint at just simply overloading the multiplyDivide
function and passing your values to that when you're ready:
function multiplyDivide(...args) {
return args.reduce((total, next, i) => (i % 2) ? (total / next) : (total * next), args.shift());
}
const values = [5, 4, 2, 3, 3];
values.push(5, 1);
console.log(multiplyDivide(...values));
It's sort of possible but you need to define the terminating condition because the problem is essentially the same problem as writing a recursive function. The function needs a way to tell whether it should return a function or a value.
How you signal the need for values is up to you. One way of doing it is to check if an argument is passed:
// Using add instead of multiplyDivide to simplify example:
function add (num) {
function adder (n) {
if (n !== undefined) {
num += n;
return adder;
}
else { // terminate
return num;
}
}
return adder;
}
Now you can do:
var sum = add(1)(2)(3)(4)();
Otherwise it would return a function which you can keep calling:
var x = add(1)(2)(3)(4);
x = x(5)(6)(7);
x = x(8)(9)(10);
var sum = x();
Since in js functions are objects, you can also implement the value getter as a static method. It won't be purely functional but makes the "API" a bit more explicit and easier to read:
function add (num) {
function adder (n) {
num += n;
return adder;
}
adder.value = function(){
return num
};
return adder;
}
Which would allow you to do:
var sum = add(1)(2)(3)(4).value();
You can even get fancy by overriding the built-in .valueOf()
and .toString()
methods:
function add (num) {
function adder (n) {
num += n;
return adder;
}
adder.valueOf = function(){
return num
};
adder.toString = function(){
return '' + num
};
return adder;
}
Which would allow you to do:
var sum = add(1)(2)(3)(4) + 5; // results in 15
var txt = add(1)(2)(3)(4) + "hello"; // results in "10hello"
The key here is that you need a way to tell the function to stop returning functions.