When I first learned JavaScript, concepts like currying felt confusing and unnecessary—until I actually started using it.
Think about ordering coffee. You don't shout everything at once.
You decide step by step — first, you choose the type of coffee, then the size, and finally the milk.
The same idea applies to currying in JavaScript.
Currying allows us to call a function like this:
orderCoffee('Latte')('Medium')('Almond milk');
Each function call captures one piece of information and returns control to another function, which waits for the next input.
Let’s look at a simple example:
function orderCoffee(type) {
return function (size) {
return function (milk) {
return `You selected ${type} ${size} with ${milk}`;
};
};
}
const orderOne = orderCoffee('Latte')('Medium')('Almond milk');
const orderTwo = orderCoffee("Americano")('Large')('No milk');
const orderThree = orderCoffee('Mocha')('Medium')('Almond milk');
Currying breaks a function into smaller functions, each taking one argument, and allows you to reuse behavior instead of repeating code.
Let’s break down what happens internally.
Step 1: const step1 = orderCoffee('Latte')
- type = 'Latte'
- Returns a function
- That function remembers the type via closure
Now the function looks like this internally
function (size) {
return function (milk) {
return `You selected Latte ${size} with ${milk}`;
};
};
Step 2: const step2 = step1('Medium')
- size = 'Medium'
- Returns another function
- Remembers size and type via closure
Now the function becomes
function (milk) {
return `You selected Latte Medium with ${milk}`;
};
Step 3: const finalStep = step2('Almond milk')
Once all arguments are provided, the function returns the final result:
const orderOne = orderCoffee('Latte')('Medium')('Almond milk');
Output:
You selected Latte Medium with Almond milk
Now let's see currying in an arrow function
const orderCoffee = (type) => (size) => (milk) =>
`You selected ${type} ${size} with ${milk}`;
const order = orderCoffee('Latte')('Medium')('Almond milk');
Arrow functions make currying look cleaner, but the internal working remains the same — let’s break it down.
Execution Flow
Step 1: order = orderCoffee('Latte')
- Returns a function waiting for size.
Now the string becomes:
You selected Latte ${size} with ${milk}
Step 2: order = orderCoffee('Latte')('Medium')
- Returns a function waiting for milk.
Now the string becomes:
You selected Latte Medium with ${milk}.
Step 3: order = orderCoffee('Latte')('Medium')('Almond milk')
All arguments are provided, and the final string is returned:
order = orderCoffee('Latte')('Medium')('Almond milk')
Output
You selected Latte Medium with Almond milk.
Summary
Currying is a functional programming technique that transforms a function with multiple parameters into a sequence of functions like (f(a,b,c) ~~~~> f(a)(b)(c)), each accepting a single argument. Currying relies heavily on closures. Each nested function retains access to the arguments provided in previous calls, allowing values to be accumulated until all required parameters are available. Once the expected number of arguments is collected, the original function executes and returns the final result.
That’s it for this article — see you in the next blog!
Top comments (0)