I'm learning right now currying in js, and particularly how to create a generic curry function. I found an example like this:
function curry(fn) {
return function curried(...args) {
console.log(args)
if (args.length < fn.length) {
return curried.bind(null, ...args);
}
return fn.apply(null, args);
};
}
This works, and when I give to this curry function a function with 3 arguments like:
function add (x, y, z) {return x + y + z;}
var addCurried = curry(add);
and then I call it like:
console.log(addCurried(1)(2)(3));
then I get 4 logs:
[1]
[1,2]
[1,2,3]
6
I understand that each time addCurried is called we thank to "curried.bind" get a new function back, which recursively calls curried function, and this in turn returns a new function and so on. That is why we have 3 calls for 3 arguments. What I can not understand how "args" argument in the inner curried function keeps increasing in elements? We do not do any push to "args" array. By the way without the rest operator the same curry function:
function curry(fn){
return function curried() {
var args = Array.prototype.slice.call(arguments);
console.log(args);
if (args.length < fn.length) {
return function (a) {
var newArgs = [].concat(args, a);
return curried.apply(null, newArgs);
}
}
return fn.apply(null, args);
}
}
is completely clear to me, because we do "var newArgs = [].concat(args, a);" and save/push the arguments manually into an array. That's why each time we call the inner curried function we get one argument more and our "args" keeps increasing.
So my question is: how in the rest operator version of the generic curry function "args" argument getting it's new elements? What is going on?
When you pass args to bind via curried.bind(null, ...args) the new function gets ...args as parameters prepended to whatever arguments are passed to it.
From the MDN docs:
arg1, arg2, ...argN Optional Arguments to prepend to arguments provided to the bound function when invoking func.
function foo(...args) {
return args;
}
console.log(foo(1)); // [1]
// prepends 2 to args
const bar = foo.bind(null, 2);
console.log(bar(1)); // [2, 1]
// prepends 3 to args, after the already-bound 2
const baz = bar.bind(null, 3);
console.log(baz(1)); // [2, 3, 1];