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

ConceptWhat It DoesExample
CurryingAlways one argument per calladd(2)(3)
Partial ApplicationPre-fill some arguments, rest can come in any groupingadd(2, 3) or add(2)(3)

When to Use Currying

  • Building specialized versions of a general function (like addGST from applyTax).
  • 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.

Leave a Comment

Your email address will not be published. Required fields are marked *