Polyfill: Create your own curry()

tusharshahi

TusharShahi

Posted on September 10, 2022

Polyfill: Create your own curry()

Saw this question in an interview some time ago:

Create a method curry that when passed in a function, will return a curried version of the function:

const join = (a,b,c) => {
  return `${a}_${b}_${c}`
}

const curriedJoin = curry(join);

console.log(curriedJoin(1)(2,3)); //1_2_3
console.log(curriedJoin(1,2)(3)); //1_2_3
console.log(curriedJoin(1)(2)()()(3)); //1_2_3
console.log(curriedJoin(1,2,3)); //1_2_3 

Enter fullscreen mode Exit fullscreen mode

What is currying?

Wikipedia says:

In mathematics and computer science, currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument.

A function that takes multiple arguments is transformed into a series of functions that take single arguments.

Initial approach

Based on the definition this is how the function is supposed to be structured:

const curry = (fn) => {

const curriedFunction = (...args) => {


};
return curriedFunction;
};
Enter fullscreen mode Exit fullscreen mode

The function takes an argument function fn and returns a new function.
The returned function expects arguments, and for ease of handling ...args (Rest Operator) is used. It is a way to handle indefinite number of arguments. Now args is an array having all the function arguments.

Returning a function

Every time the curried function is called, a function has to be returned. It is also necessary to keep track of the arguments passed at every step of the sequence. When we want to execute the function, we can call fn with ...args.

const curry = (fn) => {

  const curried = (...args) => {
    let arr = [];
      arr = [...arr,...args];
      const innerFunc = (...innerArgs) => {
      arr =[...arr,...innerArgs];
        if(arr.length === fn.length){
          return fn(...arr);
        }
        else{
          return innerFunc;
        }


     };
      return innerFunc;
    } 
    return curried;
};
Enter fullscreen mode Exit fullscreen mode

fn.length returns the number of parameters expected by fn. A javascript function can take as many arguments as passed, but this value will always return the number of formal parameters.

It is useful in the base condition. As soon as the length of the array is equal to the number of parameters expected, the callback function can be called.

Edge conditions

The above solutions solve most of the cases except this:

console.log(curriedJoin(1,2,3));
Enter fullscreen mode Exit fullscreen mode

To handle this, the function should have another check in case all the arguments are passed in the first go itself.

const curry = (fn) => {

  const curried = (...args) => {
    let arr = [];
   if(args.length === fn.length){
     return fn(...args);
   }
   else{
      arr = [...arr,...args];
      const innerFunc = (...innerArgs) => {
      arr =[...arr,...innerArgs];
        if(arr.length === fn.length){
          return fn(...arr);
        }
        else{
          return innerFunc;
        }


     };
      return innerFunc;
    } 
  }
  return curried;
}
Enter fullscreen mode Exit fullscreen mode

Summary

The question tests the solver's understanding of closures. It also relies on the fact that the solver is aware of the length property of the Function prototype, but that is not the main point. Since there is a lot of nesting it is a good way to check how clearly the solver thinks in complicated scenarios.

Let me know in comments how you would solve it?

💖 💪 🙅 🚩
tusharshahi
TusharShahi

Posted on September 10, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Loop js simplicity
javascript Loop js simplicity

April 15, 2023

Polyfill: Create your own curry()
javascript Polyfill: Create your own curry()

September 10, 2022