Understanding Currying in JavaScript: A beginner’s guide

Understanding Currying in JavaScript: A beginner’s guide

Written by steven | Published 9 months ago

There is one shared perspective about programming.

That it is exciting on the very first day, but on the thousandth, it starts to appear less interesting.

Because when you are just starting out, you’re probably diving into the cool world of syntaxes and experiencing the magic happen. But once you’ve grasped the required syntax of a particular programming language, and you sit down to actually write code, you begin to realize that writing good code involves a lot of thinking and planning stuff. 

Usually, the developers with no clear planning are so focused on solving the problem and making the application run that they put a lot of code together and make it complex. The code does its intended work but proves harder to manage at a later stage.

Also, a complex program is a perfect hideout for bugs to take shelter. So it’s considered better to break the whole code into various chunks, where each chunk is very simple and straightforward enough to handle only a particular task. 

In programming languages, these chunks are usually formed by either functions or classes. 

In JavaScript, the functions predominantly form these chunks since there are no classes in JavaScript. Even if some people would argue that there are classes in the latest version of JS, I’d like to add that those classes are nothing but wrappers on top of functions and prototypal inheritance. 

A function is nothing but a procedure that takes in input, performs some action based on the input to produce and return an output. Both taking input and returning an output are optional though. 

But think, how cool that would be if you could specifically tell those functions, “Hey! I want you to do this action, not the action already built-in in your factory.” 

Interestingly, this - passing in functions as arguments - is possible in JavaScript via special kinds of functions named higher order functions, which take in other functions (actions) as input, and can also return, if required, other functions.

Take this for example:

// Takes 'sum' function as argument
function aHof(a, b, c, sum){
   // and returns the output returned by ‘sum’
   return sum(a, b, c)
}

A higher order function (HOF) that takes a ‘sum’ function as input and returns the output produced by invocation of sum with passed parameters a, b, c. 

But that is not to say that a HOF will always take a function as an argument. To qualify as a HOF, a function needs to either take another function as an argument, or return another function, or both. 

function anotherHOF(a, b, c){
    return () => console.log(a, b, c) // returns another function
 }

If you have understood these HOFs, understanding Currying shouldn’t be a problem. 

Now the question arises.. 

What is Currying?

Currying a function, in simple words, means transforming that given function in such a way that the transformed function is callable by all its arguments one at a time, instead of all arguments stuffed inside as arguments simultaneously which is the case in general function. 

That means, if a function has three arguments, after currying it can be called by its first argument, which will return another function that takes the second argument, which returns another function that takes the last argument, which produces the final result.

generalFunc(7, 11, 5); 
curriedFunc(7)(11)(5);

To further clarify, let’s take an example. Suppose you have a function with two arguments, like this:

function doSomething(a, b){
  // does something
}

In such a case, the concept of higher order function will allow you to do something like this:

 function someHOF(func, a, b){
   return func(a, b)
 }
 const result = someHOF(doSomething, 5, 6);

And the concept of Currying will let you make a nesting like this: 

function curry(func){
  return function(a){ // Function with first argument
      return function(b){ // Function with second argument
          return func(a, b); // Original function that produces output
          }
      }
  }
}

And you can use this Currying like:

 function multiply(a, b){
    return a * b; 
 }
// Currying of ‘multiply’ function 
 const curriedFunc = curry(multiply);
 
 const result = curriedFunc(2)(3);  

Here, curriedFunc is the transformed version of the ‘multiply’ function. Such a transformed function, which is the result of Currying, is named Curried Function.

Now, you must immediately ask - What’s the benefit? Why would someone curry a function?

Advantage of Currying 

The most obvious advantage of Currying, I could give you, is that it discards the unnecessary repetition.

Suppose, you are creating a function whose sole purpose is to calculate simple interest based on the principal amount, time and rate of interest. 

function calculateInterest(p, r, t){
  return p * r * t ; 
} 

Now, also assume that the rate of interest is fixed (say 14%) and we only require to calculate interest annually, that means the time period is fixed too. 

So the first obvious thing you could do is to calculate interest amount for three people in this manner:

 const i1 = calculateInterest(4000, .14, 1)
 const i2 = calculateInterest(9000, .14, 1)
 const i3 = calculateInterest(14000, .14, 1) 

Or, you could rather curry the calculateInterest function like this: 

 function curry(f){
    return function(r){ // Fixed argument 
        return function(t){ // Fixed argument 
            return function(p){ // Variable argument 
                return f(p, r, t);
            }
        }
    }
 }
const myCurriedFunc = curry(calculateInterest); 

Note that in the nesting, the fixed arguments like rate of interest (r) and time period (t) take the above seat.

You should immediately see why that is the case.

If you are unable to, better see the implementation below: 

 const rate14Time1Formula = myCurriedFunc(.14)(1);
 
 const i1 = rate14Time1Formula(4000);
 const i2 = rate14Time1Formula(9000);
 const i3 = rate14Time1Formula(14000); 

See? 

Such an implementation makes function a reusable and configurable piece of code, which reduces the unnecessary complexity. 

And lesser the complexity, lesser the chances for bugs to hide, remember? 

Related blogs