Understanding Currying in JavaScript with an Example
Tags: javascript, programming, beginners, career
Published: 2022-01-10 04:17:15

## Overview

The [functional programming](https://en.wikipedia.org/wiki/Functional_programming) paradigm has been gaining traction in the JavaScript community for quite some time. It's hard to pinpoint when it all started, but I believe with the introduction of features like arrow functions, map, filter, reduce, etc., in ES6 (2015), we're seeing a lot more functional programming code in JavaScript.
Therefore, it would be fair to say one might expect functional programming questions in a JavaScript interview. For instance, let's take a look at the following problem statement:

> Write a function that will provide the same output regardless of whether the arguments are passed all at once or one at a time(partially).

For example:
```js
add3(1, 2, 3) // 6
add3(1)(2, 3) // 6
add3(1)(2)(3) // 6
```

The function invocation looks strange, to say the least. 
No worries, in this article, we will learn how to implement such a function using functional programming concepts.
So without further ado, let's begin.

## Basics
If we think about the `add3` function, among other things, it should somehow partially apply the arguments passed to it.
In other words, it should apply them one at a time.
In functional programming, there is a concept known as [currying](https://en.wikipedia.org/wiki/Currying#:~:text=In%20mathematics%20and%20computer%20science,each%20takes%20a%20single%20argument.).

We will use this same concept to our aid while implementing the `add3` function. Let's see how:

### Foundation

```js
/**
 * The underlying base function is "add" which takes 3 arguments and return their sum.
 */
const add = (a, b, c) => a + b + c;

/**
 * We need such a function which will transform the base function such that
 * it can also process its argument one by one.
 */
const curry = (baseFunc) => {
  // TODO: Do something with it.
};

const add3 = curry(add);
```

All the code examples are in Code Sandbox and here is the [CodeSandbox link](https://codesandbox.io/s/functional-programming-in-js-6ehkj?file=/src/index.js) to the final output. Let's get started.

### Base Case
In its simplest form, the `add3` function is equivalent to base function(`add`). In other words, the curry function will return the original function passed to it as an argument. With that in mind, let's start the implementation:

```js
/**
 * The underlying base function is "add" which takes 3 arguments and return their sum.
 */
const add = (a, b, c) => a + b + c;

/**
 * We need such a function which will transform the base function such that
 * it can also process its argument one by one.
 */
const curry = (baseFunc) => (...args) =>
  args.length === baseFunc.length ? baseFunc(...args) : curry(baseFunc);

const add3 = curry(add);
```
Let's decode the function implementation:

![base-case-demo-1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/okvvx6rmx76fiphltf8j.jpg)

Curry is a function (highlighted by the purple rectangle) that returns an anonymous function(highlighted by the green rectangle). The inner function do the following:

- aggregate all of the arguments into a single parameter named args using the [rest parameter](http://es6-features.org/#RestParameter)
- then check whether the arguments passed to it has the same length as the base function(`baseFunc`) arguments
- if that is the case, we execute the base function with the provided arguments spread using the [spread operator](http://es6-features.org/#SpreadOperator)
- otherwise, we need to carry on the process somehow, but more on that later


Now, let's understand what happens when we execute the following line of code:

```js
const add3 = curry(add);
```
The add3 gets assigned the function returned by the `curry` function with `baseFunc` param gets replaced by the argument value that is `add`:

![base case demo 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2wta9jzjtwtyz7qzjx9.jpg)

Now, let's understand how the following line of code gets evaluated to 6:

```js
add3(1,2,3);
```
Execution illustration:
![base case demo 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cn93d2j4z4wbs6pwi5xk.jpg)

When we call the `add3` function with arguments `1, 2, 3`.  They get aggregated into a single parameter named `args` as an array. Therefore, we're able to calculate the argument length which is 3 in this case.
We know it's our base case because `args.length` is equal to `add.length` therefore we return the result of `add` function by passing along the arguments. 

> Note: We do spread the arguments before passing because the underlying base function expects them individually rather than an array.

So far so good. Now, let's figure out how to make our `curry` function work for the following use cases:
- add(1)(2, 3) // 6
- add(1)(2)(3) // 6

### Recursive Case
If we were to call, `add3` as `add(1)(2,3)` using our current implementation, it would stop the execution just after the first call `add(1)`.

To handle these cases, we need to add the following ability to the curry function:
- accumulating the arguments over time (partially applying the arguments)
- chaining execution (with the help self-invoking function)

Let's see how we can achieve the desired result by rewriting the curry function.

```js
/**
 * The underlying base function is "add" which takes 3 arguments and return their sum.
 */
const add = (a, b, c) => a + b + c;

/**
 * We need such a function which will transform the base function such that
 * it can also process its argument one by one.
 */
const curry = (baseFunc, accumlatedArgs = []) => (...args) =>
  ((a) =>
    a.length === baseFunc.length ? baseFunc(...a) : curry(baseFunc, a))([
    ...accumlatedArgs,
    ...args
  ]);

const add3 = curry(add);
```

Let's not get ahead of ourselves and understand the updated `curry` function:

![final curry function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c940enjwb6mvo6gprbj7.jpg)

Curry is a function (highlighted in a purple rectangle) that returns an anonymous function(highlighted in a green rectangle) that returns another anonymous function(highlighted in a blue rectangle) that does 
the same thing that the `green` function did previously. But there are two things in this case.
- First, the curry function takes a second parameter named `accumlatedArgs` which is assigned an empty array as the [default argument value](http://es6-features.org/#DefaultParameterValues).
- Second, the innermost function(blue) is an Immediately Invoked Function Expression better known as [IFFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) and we're passing an array to it which contains all the accumulated arguments as well as the current arguments.

Now, let's understand what happens when we execute the following line of code:

```js
const add3 = curry(add);
```
`add3` gets evaluated as the following:

![add3 assignment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7gz55cwiowy1kjn51qj.jpg)

Notice that because we didn't pass any argument value for `accumulatedArgs` param, the default param value gets assigned here.

![recursive case demo 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4k69zphe6xyds8oo1dzs.jpg)

Let's take a walkthrough of the execution of `add3(1)`.
Because `accumulatedArgs` is an empty array(`[]`) and `args` is `[1]` the param `a` becomes equal to `[1]` which means the [ternary operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) condition results in false and we get:

```js
add3(1) = curry(add, [1])
```
Now, let's see the execution of `add3(1)(2,3)`:

![recursive case demo 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s61z9eds78hl0zk4cd1q.jpg)

This time `accumulatedArgs` is `[1]` and `args` is `[2,3]` so the param  `a` becomes equal to `[1,2,3]` which means this time the ternary condition results in true and we call the base function with `a`:

![recursive case demo 5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zct9a2ou5u9jy58igwey.jpg)

which is basically the base case. Logically, it's equal to:

```js
add3(1)(2,3) = add(1,2,3) = 6
```

Notice, how we accumulated the arguments that was passed into the `add3` function.

### Final Case
Now, let's also do the final case which is
```js
add3(1)(2)(3);
```
but this time we use logs in our code to see how the execution is taking place:

```js
const add = (a, b, c) => a + b + c;

const curry = (baseFunc, accumlatedArgs = []) => (...args) =>
  ((a) => {
    console.log("Received => ", JSON.stringify({ accumlatedArgs, args, a }));
    return a.length === baseFunc.length ? baseFunc(...a) : curry(baseFunc, a);
  })([...accumlatedArgs, ...args]);

const add3 = curry(add);

console.log("add3(1)(2)(3) => ", add3(1)(2)(3));
```

And as we expect, it accumulates the arguments provided to it over a while in sequential invocation. We get the following output:

```

Received =>  {"accumlatedArgs":[],"args":[1],"a":[1]} 
Received =>  {"accumlatedArgs":[1],"args":[2],"a":[1,2]} 
Received =>  {"accumlatedArgs":[1,2],"args":[3],"a":[1,2,3]} 

add3(1)(2)(3) =>  6

```


## Conclusion
As you can see, we have successfully built the solution from the ground up using first principles. The example mentioned in the article is rather straightforward but in real-world scenarios, you will encounter other use cases for currying techniques in JavaScript.
And, now, you can apply the same approach to build such a solution :)

I hope you find this article interesting and helpful.
If you did, please give it a like and share it with someone who might benefit from it.

My name is Ashutosh, and apart from working as a Full-stack engineer, I love to share my learnings with the community.
You can connect with me on [LinkedIn](https://www.linkedin.com/in/iamashutoshbhardwaj/) and follow me on [Twitter](https://twitter.com/BhardwajAshu96).

If you prefer video format please do check out my YouTube video:

{% youtube KY5LNkyc97o %}