The Secret Life of JavaScript: Currying vs. Partial Application
Source: Dev.to
The Practical Approach: Partial Application
// A standard, multi‑argument function
function multiply(a, b) {
return a * b;
}
// Manually creating partial application via a closure factory
function createMultiplier(factor) {
// We "lock in" the 'factor' argument here
return function (number) {
return number * factor;
};
}
// Usage
const double = createMultiplier(2); // We applied '2' to the first spot
console.log(double(10)); // 20
Partial Application is a manual technique: you pre‑fill some arguments of a generic function, producing a new function that awaits the remaining arguments.
Original Function: [ Slot A ] [ Slot B ] [ Slot C ]
Partial Application:
We choose to fill Slot A now.
Resulting Function: [ 10 ] [ Slot B ] [ Slot C ]
The Structural Approach: Currying
Currying is a structural transformation of a function’s signature. It converts a multi‑argument function into a chain of unary (single‑argument) functions.
const formatUrl = protocol => domain => path => `${protocol}://${domain}/${path}`;
// Partial Application – manual process
// We can fill any arguments we want, in any order we design
const addFive = (b, c) => add(5, b, c);
// Currying – structural transformation
// It forces a rigid chain: one arg at a time
const curriedAdd = a => b => c => a + b + c;
// You MUST call it like this:
curriedAdd(1)(2)(3);
Attempting to break the chain fails:
// This won't work as expected with a truly curried function:
// It returns a function expecting 'path', it does NOT use 'myblog.com' as the domain yet!
formatUrl('https', 'myblog.com');
The Analogy: The Sandwich Maker vs. The Assembly Line
The Partial Application Sandwich Maker
You have a generic sandwich recipe that needs bread, meat, and cheese. If you know today’s bread will be sourdough, you set aside the sourdough slices in advance. You’ve manually “partially applied” the bread argument, making the later steps faster.
The Curried Assembly Line
Imagine an industrial sandwich factory robot with three stations:
- Station 1 – accepts only bread and passes the result to Station 2.
- Station 2 – accepts only meat and passes the result to Station 3.
- Station 3 – accepts only cheese.
You cannot feed bread and meat to Station 1 simultaneously; the assembly line enforces a strict one‑argument‑at‑a‑time flow.
Why Did the Library Use Currying?
Because currying makes it easy to create specialized builders:
// Create a builder specifically for secure sites
const secureUrl = formatUrl('https');
// Create a builder specifically for OUR site
const myBlogUrl = secureUrl('myblog.com');
// Generating links is clean:
const post1 = myBlogUrl('post-1');
const post2 = myBlogUrl('post-2');
The curried formatUrl lets you stop the assembly line at any station, producing reusable partial functions without writing new definitions.
A Common Pitfall: The Mixed Pattern
// Not truly curried (breaks the single‑argument chain)
const add = a => (b, c) => a + b + c; // returns a function taking TWO args
// Truly curried (maintains single‑argument chain)
const curriedAdd = a => b => c => a + b + c; // each returns a function taking ONE arg
The first example is a hybrid; true currying preserves the one‑argument‑per‑function pattern throughout.
Margaret’s Cheat Sheet
Use Partial Application When:
- Writing standard JavaScript.
- Specializing a function for a specific use case (e.g.,
createMultiplier). - You need flexibility (e.g., fixing the first and third arguments while leaving the second open).
Use Currying When:
- Using a functional library (e.g., Ramda, Lodash/fp).
- You need maximum composability (piping data through a chain).
- You want to generate many specialized versions of a function automatically.
In everyday JavaScript, Partial Application appears frequently, while true Currying is rarer—but understanding the distinction helps you choose the right tool for the job.