DEV Community

Cover image for The Secret Life of JavaScript: Currying vs. Partial Application
Aaron Rose
Aaron Rose

Posted on

The Secret Life of JavaScript: Currying vs. Partial Application

Timothy was staring at his monitor with the distinct expression of someone whose brain had just finished buffering and decided to crash.

On his screen was a single line of JavaScript he had pulled from a functional programming utility library.

const formatUrl = protocol => domain => path => `${protocol}://${domain}/${path}`;
Enter fullscreen mode Exit fullscreen mode

"Margaret," Timothy said, without looking away from the screen. "My eyes are crossing. Why are there so many arrows? And why does calling it look like a nesting doll accident?"

He pointed to how the function had to be used:

formatUrl('https')('somewebsite.com')('/blog/post-1');
Enter fullscreen mode Exit fullscreen mode

Margaret rolled her chair over, coffee mug in hand. She smiled. "Ah. You've discovered the 'scary syntax' part of functional programming. You're looking at Currying."

"I thought we did this already," Timothy said. "Remember the multiplier factory? Where we created the double function?"

"Close," Margaret corrected gently. "But not quite. And the difference between what we did then and what you are seeing now is one of the most common points of confusion in JavaScript. It’s the difference between Partial Application and Currying."

The Practical Approach: Partial Application

Margaret grabbed a marker and turned to the whiteboard.

"Let's start with what we already know. The example from yesterday was 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
Enter fullscreen mode Exit fullscreen mode

"Think of Partial Application as preparation," Margaret explained. "You have a function that needs several ingredients (arguments) to work. Partial application means you add some of those ingredients now, and save the half-baked function to finish later."

She drew a simple diagram showing arguments as slots to be filled.

Original Function: [ Slot A ] [ Slot B ] [ Slot C ]

Partial Application:
We choose to fill Slot A now.
Resulting Function: [ 10 ] [ Slot B ] [ Slot C ]
Enter fullscreen mode Exit fullscreen mode

"The key thing," Margaret emphasized, "is that Partial Application is usually manual. We do it because we want to specialize a generic function. We turned a generic multiply into a specific double."

The Structural Approach: Currying

Margaret tapped the screen displaying Timothy's confusing URL code.

"This, however, is Currying. It is a structural transformation of the function's signature, not just a way to call it differently."

const formatUrl = protocol => domain => path => ...
Enter fullscreen mode Exit fullscreen mode

She wiped part of the whiteboard clean. "Currying takes a function that accepts multiple arguments and turns it into a sequence of functions that each accept exactly one argument."

"Why one?" Timothy asked.

"Because in strict functional programming, functions that take exactly one argument are the easiest things in the world to connect together, like identical Lego bricks."

She sketched out the comparison:

// 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);
Enter fullscreen mode Exit fullscreen mode

"If you try to cheat the structure," Margaret warned, "it breaks. Look what happens if you try to pass two arguments at once to that URL function:"

// 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'); 
Enter fullscreen mode Exit fullscreen mode

"With Currying, the structure demands one ingredient at a time, in order."

The Analogy: The Sandwich Maker vs. The Assembly Line

Timothy still looked a little hazy. "They still feel like they result in the same thing. You end up with a function waiting for data."

"Let's try this," Margaret said. "Imagine making sandwiches."

The Partial Application Sandwich Maker

"You make sandwiches. You have a generic recipe for a sandwich that needs bread, meat, and cheese. You know that today, everyone wants sourdough. So, you set aside fifty slices of sourdough on the counter, ready to go. You have partially applied the 'bread' argument to your sandwich process. You did this manually to make your life easier when the lunch rush hits."

The Curried Assembly Line

"Now, imagine an industrial sandwich factory robot. It is rigidly designed.

  • Station 1 only has a slot for bread. Once it gets bread, it passes the result to Station 2.
  • Station 2 only has a slot for meat. It passes the result to Station 3.
  • Station 3 only has a slot for cheese.

You cannot throw bread and meat at Station 1 simultaneously. It will break the machine."

Why did the library use Currying?

"Okay," Timothy said, slowly nodding. "But why did the library author write that URL function that way? Why restrict me to the assembly line?"

"To create specialized builders easily," Margaret answered. "Look at what you can do with that 'scary' syntax:"

// Create a builder specifically for secure sites
const secureUrl = formatUrl('https');

// Create a builder specifically for OUR site
const myBlogUrl = secureUrl('myblog.com');

// Now, generating links is clean:
const post1 = myBlogUrl('post-1');
const post2 = myBlogUrl('post-2');
Enter fullscreen mode Exit fullscreen mode

"Because the function was curried, creating those secureUrl and myBlogUrl variations was effortless. You didn't have to write new function definitions. You just stopped the assembly line at different stations."

A Common Pitfall: The Mixed Pattern

Margaret held up a warning finger. "One last thing. Because JavaScript is flexible, you will often see people mix these up."

She wrote one last example on the board:

// Not truly curried (breaks the single-argument chain)
const add = a => (b, c) => a + b + c;  // ← returns function taking TWO args

// Truly curried (maintains single-argument chain)
const curriedAdd = a => b => c => a + b + c;  // ← each returns function taking ONE arg
Enter fullscreen mode Exit fullscreen mode

"The first example is a hybrid. It takes one argument, then returns a function that takes two. It’s useful, but it’s not true currying. True currying maintains the single-argument chain all the way through."

Margaret's Cheat Sheet

Margaret tore a sheet off her notepad and sketched a quick list. "Here is how I remember when to use which."

Use Partial Application When:

  • You are writing standard JavaScript.
  • You want to specialize a function for a specific use case (like createMultiplier).
  • You need flexibility (e.g., fixing the first and third arguments, but leaving the second open).

Use Currying When:

  • You are using a functional library (like Ramda or 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," Margaret concluded, "you will see Partial Application (like our factory function) all the time. True Currying is rarer. But knowing the difference means you won't stare at that ()()() syntax and feel like your brain is crashing."


Aaron Rose is a software engineer and technology writer at tech-reader.blog and the author of Think Like a Genius.

Top comments (0)