JavaScript: Currying and Partial Application
A favorite advanced functional JavaScript question is:
What is currying, and how does it differ from partial application? How do closures support these patterns, and how would you implement a function to handle infinite currying?
Both currying and partial application are functional programming patterns that allow you to transform function signatures. They are highly valued in interviews because they test your mastery of closures, recursion, and argument collection.
1. Currying vs. Partial Application
Although often used interchangeably, they represent distinct mathematical transformations:
- Currying: A process that translates a function of multiple arguments (
f(a, b, c)) into a chain of nested functions, each accepting exactly one argument (f(a)(b)(c)). - Partial Application: A process that binds some arguments of a function immediately, returning a new function that accepts the remaining arguments (
f(a, b)(c)).
Original Function: sum(a, b, c) // Needs 3 arguments
Currying:
sum(1)(2)(3) ──► Returns nested functions of arity 1 until final value
Partial Application:
const addFive = sum.bind(null, 2, 3);
addFive(4) ──► Pre-binds 2 and 3, returns function of arity 12. Implementing a Standard Curry Helper
In JavaScript, you can implement a generic curry wrapper that converts a standard function into a curried version by checking the function's expected number of arguments (length property):
function curry(targetFn) {
return function curried(...args) {
// If we have received all expected arguments, execute targetFn
if (args.length >= targetFn.length) {
return targetFn.apply(this, args);
}
// Otherwise, return a new wrapper function to collect more arguments
return function (...nextArgs) {
return curried.apply(this, [...args, ...nextArgs]);
};
};
}
// Verification
const multiply = (a, b, c) => a * b * c;
const curriedMultiply = curry(multiply);
console.log(curriedMultiply(2)(3)(4)); // Outputs: 24
console.log(curriedMultiply(2, 3)(4)); // Outputs: 24 (Hybrid signature allowed)3. Implementing Infinite Currying
A common variation of this question is writing a function that supports infinite currying, like add(1)(2)(3)...().
To achieve this, the helper must repeatedly return a function that accumulates values until it is called with no arguments (or coercion is requested):
Signature A: Terminated by empty execution ()
function add(x) {
return function (y) {
if (y !== undefined) {
return add(x + y); // Keep accumulating
}
return x; // Return final result when executed with ()
};
}
console.log(add(1)(2)(3)(4)()); // Outputs: 10Signature B: Implicit Value Coercion (Advanced)
If you want the function to be evaluated directly without an ending empty call (e.g. add(1)(2)(3) returning 6), you must override toString or valueOf:
function addCoerce(x) {
const sumFn = (y) => addCoerce(x + y);
// Custom coercion behavior
sumFn.valueOf = () => x;
sumFn.toString = () => String(x);
return sumFn;
}
const sumVal = addCoerce(1)(2)(3);
console.log(+sumVal); // Outputs: 6 (coerced using unary plus)Senior-Level Interview Answer
Currying is a functional transformation that splits a multi-argument function of arity $N$ into a series of unary functions that resolve on the $N$-th invocation. Partial application fixes a subset of arguments ahead of execution, yielding a function that accepts the remaining parameters. Both patterns rely on JavaScript closures, which retain access to variables in their lexical environment after outer functions return. A custom curry wrapper evaluates the accumulated arguments length against the expected target function
length. If the argument quota is met, it executes the target function; otherwise, it returns a closure that merges existing arguments with incoming ones via recursion.
Common Interview Mistakes
❌ Confusing Currying with Partial Application
Saying that currying means "calling a function with fewer arguments than it expects." That describes partial application. Currying specifically refers to creating a strict chain of single-argument functions.
❌ Forgetting context binding
When implementing curried functions, failing to pass down the correct this context (e.g., omitting .apply(this, ...)). If the curried function is a method on an object, failing to propagate this will break references to other internal properties on that object.
Key Takeaways
- Closure Scope: Both patterns rely on closures to store parameters in memory across multiple execution steps.
- Strict Currying: Splitting $N$-argument calls into chains of single-argument functions (
f(a)(b)(c)). - Arity Validation: Auto-currying compares argument collection count against function signature
lengthmetadata. - Partial Binding: Freezing a set of arguments immediately to simplify downstream invocation signatures.
- Infinite Accumulation: Using recursive closures to build math functions that accumulate arguments until terminated.