Currying and Partial Application
Currying: Transforming Functions
Currying is a transformation that restructures a multi-argument function into a sequence of nested single-argument functions.
The transformation looks like this:
// Before currying: one function taking multiple arguments
function add(a, b, c) {
return a + b + c;
}
// After currying: nested functions, each taking one argument
function curriedAdd(a) {
return function (b) {
return function (c) {
return a + b + c;
};
};
}
// Or with arrow functions:
const curriedAdd = (a) => (b) => (c) => a + b + c;
Notice the structure: we’ve gone from one function with three parameters to three nested functions, each with one parameter. That’s the transformation.
Key insight: Currying changes the structure of a function. It’s a one-time transformation of how the function is defined.
Partial Application: Fixing Arguments
Partial Application is the act of calling a function with some (but not all) of its arguments, producing a new function that expects the remaining arguments.
const add = (a) => (b) => (c) => a + b + c;
// One argument at a time
const addTwo = add(2);
const addTwoAndThree = addTwo(3);
const result = addTwoAndThree(5); // 10
// Multiple arguments at once (with flexible curry implementation)
const addOneAndTwo = add(1, 2);
addOneAndTwo(3); // 6
Key insight: Partial application happens at call time. You’re not restructuring the function; you’re calling it with some arguments and getting back a function that expects the rest.
OK, but why?
Currying shines when you need to create specialized functions for APIs that expect a specific signature. Event handlers are a perfect example:
// Without currying: repetitive inline functions
<button onClick={(e) => { e.preventDefault(); dispatch('SUBMIT'); }} />
<button onClick={(e) => { e.preventDefault(); dispatch('CANCEL'); }} />
<button onClick={(e) => { e.preventDefault(); dispatch('RESET'); }} />
With currying, you can create a reusable handler factory:
const handleClick = (action) => (event) => {
event.preventDefault();
dispatch(action);
};
// Clean, reusable handlers
<button onClick={handleClick('SUBMIT')} />
<button onClick={handleClick('CANCEL')} />
<button onClick={handleClick('RESET')} />
The curried function lets you “bake in” the action while producing a function
with the exact signature onClick expects: (event) => void.
This pattern extends naturally to data transformations:
const multiply = (a) => (b) => a * b;
const double = multiply(2);
const triple = multiply(3);
[1, 2, 3].map(double); // [2, 4, 6]
[1, 2, 3].map(triple); // [3, 6, 9]
Partial application lets you create specialized versions of functions without
repeating arguments. This is powerful for building data pipelines and composing
operations with map, filter, and reduce.
Implementation
Let’s implement a curry function that transforms any multi-argument function
into a curried version. This implementation is flexible—like
lodash’s curry—and allows multiple arguments
to be passed at once.
The idea: check if we have enough arguments. Every function has a .length
property showing how many parameters it expects. If we have enough arguments,
call the original function. If not, return a new function that remembers the
arguments received so far and waits for more. When called again, it combines old
and new arguments and repeats the check.
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function (...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
Dry Run: Tracing add(1)(2)(3)
Let’s trace through exactly what happens when we call our curried add function: