Masterbook/Chapter 59• 3 min read
View Raw Code on GitHub

Explain currying and partial application

Explain currying and partial application

Think of a multi-argument function like a machine with several input slots. Currying turns that machine into a series of single-slot machines chained together. Each call fills one slot and returns a new function that expects the next input. The original function only runs when all slots have been filled.

In concrete terms, if you start with a function f(a, b, c), a curried version would look like f(a)(b)(c). The function doesn't execute until you've provided all three arguments. That might seem strange at first, but it allows you to create partially specialized versions of the function by "locking in" some of the inputs.
Here's the earlier example, annotated:

// A normal 3-argument function
function sum(a, b, c) {
    return a + b + c;
}
// A curried version
function currySum(a) {
    return function (b) {
        return function (c) {
            return a + b + c;
        };
    };
}
// Fill the arguments one by one
const add1 = currySum(1); // returns a function waiting for b and c
const add1and2 = add1(2); // returns a function waiting for c
console.log(add1and2(3)); // prints 6 (1+2+3)

A helper function can automate currying so you don't have to write nested functions by hand, but the idea is the same: you gradually supply arguments until the function has enough to run.

Partial application

Partial application is a looser technique: it lets you pre-fill some arguments of a function to create a new function. Unlike curried functions, the partially applied function still expects all remaining arguments at once. In the example you provided:

function multiply(a, b, c) {
    return a * b * c;
};

function partial(fn, ...fixedArgs) {
    return function (...restArgs) {
        return fn(...fixedArgs, ...restArgs);
    };
};

const doubleAndTriple = partial(multiply, 2, 3); // fixes a = 2, b = 3
console.log(doubleAndTriple(4)); // 24 (2 * 3 * 4)

Here, doubleAndTriple is a new function where two of the original three arguments are pre-set, and you only need to supply the last one. Partial application can also be done with built-in methods like Function.prototype.bind(), which fixes the this context and leading arguments.

When are these useful?

  • Creating specialized functions: Suppose you have a logging function log(level, namespace, message). You can curry or partially apply it to make a debugLog = log.bind(null, 'debug', 'MyApp') so you only need to supply the message.
  • Function composition: In functional programming, you often build complex behavior by composing small functions. Curried functions make composition easier because they always return unary functions.
  • Reusable configuration: If a function accepts many configuration options, currying lets you create pre-configured versions for different situations (e.g., makeApiCall(baseURL)(endpoint)(params)).
  • Readable callbacks: Sometimes event handlers or array methods require a callback with a single parameter. Currying can wrap a multi-argument function into the expected signature while keeping the original logic intact. For beginners, it helps to see currying and partial application as ways to reuse and adapt functions without rewriting them. Currying breaks a function into a chain of one-argument steps, while partial application "locks in" some arguments and returns a function that expects the rest. Both are powerful techniques for writing flexible, declarative code once you get comfortable with passing functions around.