DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,673 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Polyfill: Create your own curry()
TusharShahi
TusharShahi

Posted on • Updated on

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?

Top comments (5)

Collapse
 
adam_cyclones profile image
Adam Crockett

My answer would be:

No I won't write it, currying is an inefficient antipattern in this example and then I'd walk out 😊

Thanks for a well written post though, the concept might sound confusing.

Collapse
 
tusharshahi profile image
TusharShahi Author

God, you must be fun at interviews :P
Just kidding.
Honestly, I have also not used something like this in real world.

Collapse
 
adam_cyclones profile image
Adam Crockett

Under the right circumstances the strategy of calling out the drawbacks, running the interview (but doing the technical task well) can lead to a stronger position. And no, no I'm no fun 😊

Thread Thread
 
tusharshahi profile image
TusharShahi Author

True. That makes sense.

Collapse
 
tusharshahi profile image
TusharShahi Author

Follow up : How would you change your code if you have to handle this case too:

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

"I made 10x faster JSON.stringify() functions, even type safe"

☝️ Must read for JS devs