JavaScript a clever way to implement currying

In this article, we are going to explore two different implementations of currying in JavaScript.

First example

Let's consider first

function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      return func.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2));
      }
    }
  };
}

Here's how we use it

function sum(a, b, c) {
  return a + b + c;
}

let curriedSum = curry(sum);

alert( curriedSum(1, 2, 3) ); // 6, still callable normally
alert( curriedSum(1)(2,3) ); // 6, currying of 1st arg
alert( curriedSum(1)(2)(3) ); // 6, full currying

First, from let curriedSum=curry(sum) we get function curried(...args) returned to call upon subsequently. In fact, curriedSum(1,2,3) is in effect calling curried(1,2,3).

If we call curriedSum(1,2,3), from the if statement we can see that we are simply executing sum(1,2,3). This is because func.length==args.length in this case (which is 3).

If we are doing partial calls like curriedSum(1), we are now executing else portion of the statement and we get returned function(...args2). So if we proceed to do curriedSum(1)(2,3), in effect we are doing function(2,3) and this calls our function curried again (curried.apply(this, args.concat(args2))). But this time we are passing ...[1,2,3] as arguments to our curried function (due to concat). So this time we are calling curried(1,2,3). But now args.length equals func.length which is 3 arguments required by sum function. So, this time we are calling sum(1,2,3) and 6 is returned.

So curried function was used recursively to test if all required arguments to sum function were supplied (in which case sum function is evaluated), and if not, construct the next partial function.

For instance, in our example we first called curriedSum(1) then curriedSum(1)(2,3) which finally resulted in 6.

Second example.

const curry =(fn) =>{
    return curried = (...args) => {
        if (fn.length !== args.length){
            return curried.bind(null, ...args)
        }
       return fn(...args);
    };
}
const totalNum=(x,y,z) => {
    return x+y+z 
} 
const curriedTotal = curry(totalNum);
console.log(curriedTotal(10) (20) (30));

Let's start with curriedTotal(10). This triggers calling of function curried(10). Since not all arguments to function totalNum is yet provided, we execute what's inside if statement. This uses bind call to return a copy of curried(10). That is a copy of function curried with the first argument of 10 provided.

Next is curriedTotal(10)(20). This calls curried_copy(10,20). Again we create another copy of curried_copy(10,20) and return it as a function.

Next, we do curriedTotal(10)(20)(30). This calls curried_copy_of_copy(10,20,30). Now fn.lengh==args.length, which is 3 in this case, so it now returns totalNum(10,20,30) which is 60.