Currying Functions in JavaScript
Who is Curry?
Three mathematical logicians, across two different centuries, and each with names of varying friendliness to English-speaking mouth-and-tongue shapes, iterated on a technique that allowed functions to be more modular. The first guy was named Gottlob Frege. Had he finalized this concept, we would call this process fregeing (FRAY-guh-ing). Probably for the best that we don’t.
The second guy was Moses Schönfinkel. Yes, some people, but not most, call this process schönfinkeling. Fun to say, fun to spell, and just fun to look at; a perfect safe-word candidate.
Haskell Brooks Curry was born on September 12, 1900, to not only finalize this concept, but also its nomenclature. Historians refer to this technique as currying, though haskelling or brooksing might also have been fun alternatives.
What is to curry ?
"Who's gonna curry the boats and logs?"
—a hungry and possibly vegetarian 100Devs engineer
Currying is an advanced functional programming technique which transforms a function that accepts multiple parameters into multiple functions that accept a single parameter:
Single function, multiple parameters:
f(a,b,c)
Multiple functions, which each take a single parameter:
f(a)(b)(c)
This is possible in JavaScript because JavaScript functions are first-class members. Flying first-class in JavaScript includes such member perks as:
1. JS functions may be passed as arguments into other functions.
const array = [1, 2, 3, 4, 5, 6, 7];
const odds = array.map( num => num % 2 === 1); //.map takes this function as an argument.
- JS Functions can return other JS functions (which in techy parlance constitutes a closure, which will be a whole other blog post entirely).
function outer () {
return function inner() {
console.log('Hello!');
}
}
- JS Functions can be assigned to variables.
const funcExpression = () => console.log('express yourself')
Function currying harnesses these different aspects of JS functions’ first-classdom. Let’s start with the obligatory pedagogically-helpful-but-functionally-useless example. The following function sum
is a regular-degular function declaration:
function sum (a,b,c){
return a + b + c
}
sum(2, 3, 4) // 9
How to curry, step-by-step
Let's curry the initial function sum
. Remember that currying will turn a single function with multiple parameters into multiple functions with single parameters.
The first function passes the first parameter into the function curriedSum
, which wraps the whole shebang:
function curriedSum (a) {
//there's nothing in here yet!
}
This outer function returns an anonymous function, which passes in the next parameterb
:
function curriedSum (a) {
//return the next function with the next param
return function (b) {
//nothing in here yet!
}
}
And finally, the final parameter c
is passed into the final returned anonymous function, which in turn, contains the exact same return statement from the original, non-curried function.
function curriedSum (a) {
return function (b) {
return function (c) {
return a + b + c;
}
}
}
We can call the curried function like so:
function curriedSum (a) {
return function (b) {
return function (c) {
return a + b + c;
}
}
}
curriedSum(2)(3)(4); // 9
Why curry?
The key benefit of function currying is modularity, or in other words, LEGO-like. If you "partially call" the curried sum function from above, you can create functions like so:
function curriedSum (a) {
return function (b) {
return function (c) {
return a + b + c
}
}
}
const addFive = curriedSum(2)(3) // can take one more argument
addfive(15) // 20
Storing the function into addFive
is what theorists call "partial application".
But to be honest, I'm tired of the mathematical example. Let's create a curried function that generates different kinds of curries.
Currying a "curry" function
"Now you're currying with curry."
A curry is a dish with a sauce seasoned with spices (thanks, Wikipedia). It was derived from the Tamil word for "sauce" (kari / கறி), which British people then applied to any saucy dish that had a modicum of spice in it (thanks, colonialism). Curry is not a monolith, curry is not a taxonomy; curry comes in many flavors, forms, and is served alongside many different sides, which makes it perfect for this (pedagogically-helpful, perhaps-still-functionally-useless, but-FUN!) example.
Below, the function curry
takes four arguments: demonym (adjective of origin, i.e. "Japanese", "Californian", "Geordie"), sauce, ingredient, and starch. It returns a string that describes the curry I'm making for dinner tonight.
function curry (demonym, sauce, ingredient, starch) {
return `I am making ${demonym} curry with a ${sauce} sauce,
${ingredient}, and some ${starch}.`
}
const indianCurry = curry("Indian", "tomato", "chicken", "roti");
//"I am making Indian curry with a tomato sauce, chicken, and some roti."
const thaiCurry = curry("Thai", "spicy green", "shrimp", "rice");
//I am making a Thai curry with spicy green sauce, shrimp, and some rice."
const jamaicanCurry = curry("Jamaican", "spicy", "goat", "rice & peas");
//I am making a Jamaican curry with spicy sauce, goat, and some rice & peas.
This function works as expected, but the problem is that you can't change out any of the components of the curries without calling the entire function over again. Want a thaiCurry
the exact same way above, but with chicken instead of shrimp? You have to create an entirely new variable, switching out a single argument and leaving all the rest the same. Same goes for other swapped out ingredients:
const greenThaiCurryChicken = curry("Thai", "spicy green", "chicken", "rice");
const greenThaiCurryShrimp = curry("Thai", "spicy green", "shrimp", "rice");
const greenThaiCurryTofu = curry("Thai", "spicy green", "tofu", "rice");
const redThaiCurryTofu = curry("Thai", "spicy red", "tofu", "rice");
const redThaiCurryChicken = curry("Thai", "spicy red", "chicken", "rice");
And so on. Not very DRY, is it? Wouldn't it be nice if we could create smaller, more modular pieces of our curry function? Something like...
const greenThai = curry("Thai", "green")
//I am making a Thai curry with green sauce, undefined, and some undefined.
const indian = curry("Indian", "spicy")
//I am making a Indian curry with spicy sauce, undefined, and some undefined.
"Indian curry with spicy sauce, undefined, and some undefined" may have been what actual British people were thinking when they tried a spicy dish with sauce for the first time.
Our dreams aren't yet crushed, for currying makes modularizing a function possible! Let's take a look at the curried version of the curry function. I've added console.log statements to each of the parameter levels for ease of understanding:
function curriedCurry(demonym) {
console.log(`I am making ${demonym} curry.`);
return function (sauce) {
console.log(`I am making ${demonym} curry with ${sauce} sauce.`);
return function (ingredient) {
console.log(
`I am making ${demonym} curry with ${sauce} and ${ingredient}`
);
return function (starch) {
console.log(`I am making ${demonym} curry with ${sauce} sauce, ${ingredient}, and some ${starch}`);
}
}
}
}
This has more nests than an African Social Weaver, but we can tweak it a bit later. For now, let's focus on what happens when the functions are called with curry syntax, one parameter at a time.
curriedCurry("Thai");
//I am making Thai curry.
curriedCurry("Thai")("red chili coconut");
//I am making Thai curry.
//I am making Thai curry with red chili coconut sauce.
curriedCurry("Thai")("red chili coconut")("shrimp");
//I am making Thai curry.
//I am making Thai curry with red chili coconut sauce.
//I am making Thai curry with red chili coconut sauce and shrimp.
curriedCurry("Thai")("red chili coconut")("shrimp")("rice");
//I am making Thai curry.
//I am making Thai curry with red chili coconut sauce.
//I am making Thai curry with red chili coconut sauce and shrimp.
//I am making Thai curry with red chili coconut sauce, shrimp, and some rice.
You might notice that each additional parameter shows the console.log statement of the previous nested function. This makes sense because we're returning each successive function in order to call it, which is easier to observe when we store the partial functions into variables (remember: first class!).
Let's make a less spicy yellow curry and store it in a variable:
const yellowThai = curriedCurry("Thai")("mild yellow");
//I am making Thai curry.
//I am making Thai curry with mild yellow sauce.
We can then add whichever ingredient we like to the yellowThai
variable by calling it. Remember: each "partial application" returns a function!
yellowThai('chicken');
//I am making Thai curry with mild yellow sauce and chicken.
yellowThai('tofu')('rice');
//I am making Thai curry with mild yellow sauce and tofu.
//I am making Thai curry with mild yellow sauce, tofu, and rice.
Just imagine the many different kinds of curries that we can make. Thanks to this technique, our curry (and code) feels endlessly modular! Thanks to our curriedCurry
function we can store an "Indian curry with creamy tomato sauce" into the variable tikkaMasala
, then create vegetarian and meat-based versions of it by calling their respective parameters!
const tikkaMasala = curriedCurry("Indian")("creamy tomato");
const vegetarianTikka = tikkaMasala("paneer");
const chickenTikka = tikkaMasala("chicken");
chickenTikka('naan')
//I am making Indian curry with creamy tomato sauce, chicken, and some naan.
vegetarianTikka('rice')
//I am making Indian curry with creamy tomato sauce, paneer, and some rice.
Takeaways
or, what British people call the boxes when they order all this damn curry
JavaScript functions are powerful (first class!).
Currying functions can allow for modularity through partial application (LEGO pieces!).
Curry is delicious and you should probably have some for your next meal.
Remember our hellish nested curry function? Thanks to ES6 arrow syntax, we can really prune that thing.
Let's remove the successive console.log statements, and apply some ES6 magic:
const curriedCurry = demonym => sauce => ingredient => starch => {
return `I am making ${demonym} curry with ${sauce} sauce, ${ingredient}, and some ${starch}.`
}
I hope I've helped you to understand the benefits of currying functions! (Or maybe I just made you hungry for this wondrous, versatile, and complex culinary miracle born of Southeast Asia).
Further Reading
JavaScript.info's tutorial on currying
Also, check out their tutorial on closure, which makes currying possible
Dave Gray's video on currying, which makes use of ES6 syntax
This curry recipe which changed my life
Subscribe to my newsletter
Read articles from Steven Moses Ilagan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Steven Moses Ilagan
Steven Moses Ilagan
Before 2020 I was a freelance musician, working with hundreds of people to bring countless ideas to life. I've taken that creativity & team-oriented spirit to the world of software, creating real projects that help others. Take a gander on my portfolio! Besides music, I love reading & writing. My favorite authors include Toni Morrison, Jonathan Franzen, Elizabeth Strout, Lucy Ellmann, George Saunders, and Patricia Lockwood.