JavaScript Currying
Currying transforms a function that takes multiple arguments into a chain of functions that each take one argument. Instead of calling add(2, 3), a curried version works as add(2)(3). Each call returns a new function that waits for the next argument.
A Simple Analogy
Think of a vending machine. You press the category button first (drinks), then the item button (cola), then the size button (large). Each step locks in one choice before moving to the next. Currying works the same way — one argument at a time, building toward the final result.
Diagram: Normal vs Curried Function
Normal:
add(2, 3) ──► 5
Curried:
add(2) ──► returns a function waiting for next arg
(3) ──► 5
Vending Machine:
machine("drinks")("cola")("large") ──► "Large Cola"
Writing a Curried Function
// Normal function
function add(a, b) {
return a + b;
}
// Curried version
function curriedAdd(a) {
return function(b) {
return a + b;
};
}
let add5 = curriedAdd(5); // locks in a = 5
console.log(add5(3)); // 8
console.log(add5(10)); // 15
console.log(curriedAdd(2)(7)); // 9
Calling curriedAdd(5) does not give you a number yet — it gives you a specialized function that always adds 5 to whatever you pass next.
Arrow Function Shorthand
Arrow functions make curried functions very compact.
const add = a => b => a + b;
console.log(add(3)(4)); // 7
const add10 = add(10);
console.log(add10(5)); // 15
console.log(add10(20)); // 30
Diagram: Arrow Currying Step by Step
add(3) → b => 3 + b (a new function, a is locked at 3) │ ▼ add(3)(4) → 3 + 4 → 7
Three-Argument Curried Function
const volume = length => width => height => length * width * height;
console.log(volume(2)(3)(4)); // 24
let fixedLength2 = volume(2);
let fixedLength2Width3 = fixedLength2(3);
console.log(fixedLength2Width3(5)); // 30
console.log(fixedLength2Width3(10)); // 60
Diagram: Three-Step Currying
volume(2) → fn waiting for width volume(2)(3) → fn waiting for height volume(2)(3)(4) → 2 × 3 × 4 = 24
Practical Use: Partial Application
The biggest benefit of currying is partial application — pre-loading a function with some arguments to create a specialized version.
const multiply = a => b => a * b;
const double = multiply(2);
const triple = multiply(3);
const tenTimes = multiply(10);
console.log(double(6)); // 12
console.log(triple(6)); // 18
console.log(tenTimes(6)); // 60
You write the multiplication logic once. Then you create specialized tools — double, triple, tenTimes — without writing new functions.
Real-World Example: Tax Calculator
const applyTax = taxRate => price => price + (price * taxRate / 100);
const addGST = applyTax(18);
const addVAT = applyTax(5);
console.log(addGST(1000)); // 1180
console.log(addVAT(1000)); // 1050
console.log(addGST(500)); // 590
Diagram: Tax Calculator
applyTax(18) ──► addGST function (18% tax locked in)
│
addGST(1000) ──► 1180
addGST(500) ──► 590
applyTax(5) ──► addVAT function (5% tax locked in)
│
addVAT(1000) ──► 1050
A Generic curry Helper
You can write a helper that turns any function into a curried version automatically.
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
}
return function(...moreArgs) {
return curried(...args, ...moreArgs);
};
};
}
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
console.log(curriedAdd(1, 2, 3)); // 6
How the Helper Works
curry(add) wraps add in logic that says: "If I have all the arguments I need → run the function" "If not → return a function to collect more arguments" fn.length = 3 (add needs 3 arguments) curriedAdd(1)(2)(3): call 1: args = [1] → not enough, return new fn call 2: args = [1,2] → not enough, return new fn call 3: args = [1,2,3] → enough! run add(1,2,3) = 6
Currying vs Partial Application
| Concept | What It Does | Example |
|---|---|---|
| Currying | Always one argument per call | add(2)(3) |
| Partial Application | Pre-fill some arguments, rest can come in any grouping | add(2, 3) or add(2)(3) |
When to Use Currying
- Building specialized versions of a general function (like
addGSTfromapplyTax). - Creating reusable function pipelines.
- Working with functional programming libraries like Ramda or Lodash/fp.
- Making event handler factories in UI code.
Summary
Currying splits a multi-argument function into a chain of single-argument functions. Each call locks in one value and returns a new function waiting for the next. This lets you create specialized tools from general functions without repeating code. The real power appears when you build partial applications — pre-configured functions ready for reuse across your program.
