Currying and Partial Application in JavaScript
In my current journey to improve my knowledge of frontend JavaScript, I decided to read through Douglas Crockford’s book JavaScript: The Good Parts. A few of the functional features piqued my interest, and in particular, currying.
Currying allows a function to be broken up into nested functions, each with some of the arguments from the original signature. This can be useful for binding data to certain arguments of a function for later reusability. We’ll start with an example of a perimeter calculator for regular (all sides are equal length) shapes.
function calculatePerimeter(name, sides, length) { return ’This ’ + name + ' has ' + sides + ' sides and a perimeter of ' + sides*length; }; output = calculatePerimeter(“Triangle”, 3, 10); // This Triangle has 3 sides and a perimeter of 30
Every time we want a perimeter of a given shape, we need to pass in all three arguments. But what if we’re only dealing with triangles for a given period of time? Breaking calculatePerimeter
into nested function calls would allow us to accommodate this.
function calculatePerimeter (name) { return function (sides) { return function (length) { return 'This ' + name + ' has ' + sides + ' sides and a perimeter of ' + sides*length; } } } //Using arrow notation var calculatePerimeter = name => sides => length => 'This ' + name + ' has ' + sides + ' sides and a perimeter of ' + sides*length;
This function is now considered ‘curried.’ We can feed in arguments one by one with subsequent calls and be given back a function each time until all arguments are filled. Now let’s create a trianglePerimeter
function.
var trianglePerimeter = calculatePerimeter(“Triangle”)(3); var perimeterA = trianglePerimeter(9); //This Triangle has 3 sides and a perimeter of 27 var perimeterB = trianglePerimeter(6) //This Triangle has 3 sides and a perimeter of 18
We created a reusable function to get the perimeter specifically for triangles and we only sent arguments to calculatePerimeter
once. Since we broke up the original calculatePerimeter
into a separate function for each argument, we had to invoke one function to set name
and another to set sides
. This is the reason for two sets of parentheses at calculatePerimeter
: after we send the name we are returned a new function awaiting the number of sides, and we immediately call it with 3. A final function is returned only needing the length, which we set to trianglePerimeter
.
Breaking functions up by hand like this can be quite annoying and ugly. Plus, we might only want a curried version of a function in specific cases. Crockford provides a way to handle on-the-fly currying by modifying the Function
prototype. From the book:
Function.prototype.curry = function() { var slice = Array.prototype.slice, args = slice.apply(arguments), that = this; return function() { return that.apply(null, args.concat(slice.apply(arguments))); }; };
This method actually allows multiple arguments to be given to bind several parameters at once. Now, using our original calculatePerimeter
function, we can achieve the effects of a currying whenever we need them.
function calculatePerimeter(name, sides, length) { return ’This ’ + name + ' has ' + sides + ' sides and a perimeter of ' + sides*length; }; var hexagonPerimeter = calculatePerimeter.curry(“Hexagon", 6); perimeter = hexagonPerimeter(7); //This Hexagon has 6 sides and a perimeter of 42
It should be noted that even though Crockford named this method ‘curry,’ this is actually a different yet related concept called ‘partial application.’ True currying in the mathematical sense requires there to be a series of one-argument/1-arity functions for each argument in the original. A new function for the next argument is received from each call, until the last one is satisfied. Partial application allows some of the parameters of a function to be filled, and returns a new function asking for the rest of the remaining functions. With this curry
function we can do:
mysteryPerimeter = calculatePerimeter.curry(“Mystery Shape”); perimeter = mysteryPerimeter(5, 12); //This Mystery Shape has 5 sides and a perimeter of 60
mysteryPerimeter
is not a chain of functions, but instead a single function that you can call with the remaining missing parameters. “Real” currying can be emulated by making repeated calls to curry with a singular argument. In practice, there are probably not too many instances where currying is actually needed over partial application. During my time looking through online communities, the two terms are often confused and used interchangeably — at least when discussing JS.
The curry package provides a hybrid solution. Functions are curried in the true sense, but are also allowed to be used with partial application. Using our example:
curriedPerimeter = curry(calculatePerimeter); perimeter = curriedPerimeter(“Triangle”)(3)(10); perimeter = curriedPerimeter(“Triangle”, 3)(10); perimeter = curriedPerimeter(“Triangle”)(3, 10); perimeter = curriedPerimeter(“Triangle”, 3, 10);
All of these are valid and achieve the same result. It bridges the gap between currying and partial application — and might add to the confusion of the two concepts.
Being able to use curried and partially-applied functions adds a large amount of flexibility to your code. More abstract functions can be configured into smaller, more easily understood functions. And, of course, lots of repetition can be avoided.
Photo credit: Keith Allison from Hanover, MD, USA – Stephen Curry
CC BY-SA 2.0