What is currying?
Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each take a single argument.
So currying transforms fn(a, b, c…n) to fn(a)(b)(c)…n) . Let’s dive right in to an example.
const sum = (a, b) => a + b;
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)); // = 3
The curriedSum function converts the sum function into a sequence of two functions since the original sum function needs two arguments.
Naive implementation
Based on the above example, we can implement a naive version of the curry function. So curriedSum returns a function which accepts one argument b. This is passed as the second argument to the function being curried. The first argument is stored in the function closure.
function curry(fn) {
  return function (a) {
    return function (b) {
      return fn(a, b);
    };
  };
}
Currying with variadic arguments
The previous implementation has a few problems
- It only works for functions which accept 2 arguments.
 - The context value is not being propagated accurately.
 
Let’s try implementing a generic currying function which supports variadic arguments.
There are a couple of key insights behind the new implementation.
- Function.length returns the arity of the function. The arity of a function is the number of parameters expected by the function.
 
For example, consider this multiply function which returns the product of three numbers;
const multiply = (a, b, c) => a * b * c;
console.log(multiply.length); // 3
- We have to keep collecting arguments until we have enough arguments, i.e. till the number of arguments are greater than or equal to the arity of the function.
 
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      // Execute the original function when we have enough arguments
      return fn.apply(this, args);
    } else {
      return (...nextArgs) => curried(...args, ...nextArgs); // otherwise keep on adding arguments
    }
  };
}
We can use bind for a more concise implementation.
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return curried.bind(this, ...args);
    }
  };
}
Infinite sum
This happens to be a popular interview question based on recursion. So here’s the infinite sum function that we have to implement
sum(1)() // 1
sum(3)(2)() // 5
sum(1)(2)...(n)() // (1 + 2 + 3 ...n)
We can make some observations based on the above example
- The sum function returns the sum when we call it with undefined.
 - If the function is called with a valid number another function is returned
 
const sum = (a) => {
  return (b) => {
    if (b === undefined) {
      return a;
    } else {
      return sum(a + b);
    }
  };
};
Infinite sum with variadic arguments
sum(1, 2)(3)(4, 5)(); //15
This is a bit more tricky. We are not dealiing with single arguments anymore.Try implementing this on your own before checking the solution.
Solution
const sum = (...args) => {
  return (...moreArgs) => {
    if (moreArgs.length === 0) {
      return args.reduce((a, b) => a + b);
    } else {
      return sum(...args, ...moreArgs);
    }
  };
};
Currying with placeholder support
We are not done with currying yet, currying with placeholder support is something I might tackle in the future.