JavaScript curry: what are the practical applications?
Solution 1
@Hank Gay
In response to EmbiggensTheMind's comment:
I can't think of an instance where currying—by itself—is useful in JavaScript; it is a technique for converting function calls with multiple arguments into chains of function calls with a single argument for each call, but JavaScript supports multiple arguments in a single function call.
In JavaScript—and I assume most other actual languages (not lambda calculus)—it is commonly associated with partial application, though. John Resig explains it better, but the gist is that have some logic that will be applied to two or more arguments, and you only know the value(s) for some of those arguments.
You can use partial application/currying to fix those known values and return a function that only accepts the unknowns, to be invoked later when you actually have the values you wish to pass. This provides a nifty way to avoid repeating yourself when you would have been calling the same JavaScript built-ins over and over with all the same values but one. To steal John's example:
String.prototype.csv = String.prototype.split.partial(/,\s*/);
var results = "John, Resig, Boston".csv();
alert( (results[1] == "Resig") + " The text values were split properly" );
Solution 2
Here's an interesting AND practical use of currying in JavaScript that uses closures:
function converter(toUnit, factor, offset, input) { offset = offset || 0; return [((offset + input) * factor).toFixed(2), toUnit].join(" "); } var milesToKm = converter.curry('km', 1.60936, undefined); var poundsToKg = converter.curry('kg', 0.45460, undefined); var farenheitToCelsius = converter.curry('degrees C', 0.5556, -32); milesToKm(10); // returns "16.09 km" poundsToKg(2.5); // returns "1.14 kg" farenheitToCelsius(98); // returns "36.67 degrees C"
This relies on a curry
extension of Function
, although as you can see it only uses apply
(nothing too fancy):
Function.prototype.curry = function() { if (arguments.length < 1) { return this; //nothing to curry with - return function } var __method = this; var args = toArray(arguments); return function() { return __method.apply(this, args.concat([].slice.apply(null, arguments))); } }
Solution 3
Agreeing with Hank Gay - It's extremely useful in certain true functional programming languages - because it's a necessary part. For example, in Haskell you simply cannot take multiple parameters to a function - you cannot do that in pure functional programming. You take one param at a time and build up your function. In JavaScript it's simply unnecessary, despite contrived examples like "converter". Here's that same converter code, without the need for currying:
var converter = function(ratio, symbol, input) {
return (input*ratio).toFixed(2) + " " + symbol;
}
var kilosToPoundsRatio = 2.2;
var litersToUKPintsRatio = 1.75;
var litersToUSPintsRatio = 1.98;
var milesToKilometersRatio = 1.62;
converter(kilosToPoundsRatio, "lbs", 4); //8.80 lbs
converter(litersToUKPintsRatio, "imperial pints", 2.4); //4.20 imperial pints
converter(litersToUSPintsRatio, "US pints", 2.4); //4.75 US pints
converter(milesToKilometersRatio, "km", 34); //55.08 km
I badly wish Douglas Crockford, in "JavaScript: The Good Parts", had given some mention of the history and actual use of currying rather than his offhanded remarks. For the longest time after reading that, I was boggled, until I was studying Functional programming and realized that's where it came from.
After some more thinking, I posit there is one valid use case for currying in JavaScript: if you are trying to write using pure functional programming techniques using JavaScript. Seems like a rare use case though.
Solution 4
I found functions that resemble python's functools.partial
more useful in JavaScript:
function partial(fn) {
return partialWithScope.apply(this,
Array.prototype.concat.apply([fn, this],
Array.prototype.slice.call(arguments, 1)));
}
function partialWithScope(fn, scope) {
var args = Array.prototype.slice.call(arguments, 2);
return function() {
return fn.apply(scope, Array.prototype.concat.apply(args, arguments));
};
}
Why would you want to use it? A common situation where you want to use this is when you want to bind this
in a function to a value:
var callback = partialWithScope(Object.function, obj);
Now when callback is called, this
points to obj
. This is useful in event situations or to save some space because it usually makes code shorter.
Currying is similar to partial with the difference that the function the currying returns just accepts one argument (as far as I understand that).
Solution 5
Consider filter
function. And you want to write a callback for it.
let x = [1,2,3,4,5,6,7,11,12,14,15];
let results = x.filter(callback);
Assume want to output only even numbers, so:
let callback = x => x % 2 === 0;
Now imagine we want to implement our callback
such that
depending on scenario it outputs even numbers which are above some threshold number (such
number should be configurable).
We can't easily make such threshold number a parameter to callback
function, because filter
invokes callback
and by default passes it array elements and index.
How would you implement this?
This is a good use case for currying:
let x = [1,2,3,4,5,6,7,11,12,14,15];
let callback = (threshold) => (x) => (x % 2==0 && x > threshold);
let results1 = x.filter(callback(5)); // Even numbers higher than 5
let results2 = x.filter(callback(10)); // Even numbers higher than 10
console.log(results1,results2);
Related videos on Youtube
Dave Nolan
Updated on July 30, 2021Comments
-
Dave Nolan almost 3 years
I don’t think I’ve grokked currying yet. I understand what it does, and how to do it. I just can’t think of a situation I would use it.
Where are you using currying in JavaScript (or where are the main libraries using it)? DOM manipulation or general application development examples welcome.
One of the answers mentions animation. Functions like
slideUp
,fadeIn
take an element as an arguments and are normally a curried function returning the high order function with the default “animation function” built-in. Why is that better than just applying the higher-up function with some defaults?Are there any drawbacks to using it?
As requested here are some good resources on JavaScript currying:
- http://www.dustindiaz.com/javascript-curry/
- Crockford, Douglas (2008) JavaScript: The Good Parts
- http://www.svendtofte.com/code/curried_javascript/ (Takes a detour into ML so skip the whole section from “A crash course in ML” and start again at “How to write curried JavaScript”)
- http://web.archive.org/web/20111217011630/http://blog.morrisjohns.com:80/javascript_closures_for_dummies
- How do JavaScript closures work?
- http://ejohn.org/blog/partial-functions-in-javascript (Mr. Resig on the money as per usual)
- http://benalman.com/news/2010/09/partial-application-in-javascript/
I’ll add more as they crop up in the comments.
So, according to the answers, currying and partial application in general are convenience techniques.
If you are frequently “refining” a high-level function by calling it with same configuration, you can curry (or use Resig’s partial) the higher-level function to create simple, concise helper methods.
-
ethan over 15 yearscan you add a link to a resource that describes what JS currying is? a tutorial or a blog post would be great.
-
danio over 15 yearssvendtofte.com is longwinded but if you skip the whole section from "A crash course in ML" and start again at "How to write curried JavaScript" it becomes a great introduction to currying in js.
-
gsklee almost 11 yearsThis is a good starting point to understand what curry and partial application really is: slid.es/gsklee/functional-programming-in-5-minutes
-
phatskat over 10 yearsThe link to
svendtofte.com
looks to be dead - found it on the WayBack machine though at web.archive.org/web/20130616230053/http://www.svendtofte.com/… Sorry, blog.morrisjohns.com/javascript_closures_for_dummies seems to be down too -
RobG about 10 yearsBTW, Resig's version of partial is deficient (certainly not "on the money") in that it will likely fail if one of the pre–initialised ("curried") arguments is given the value undefined. Anyone interested in a good currying function should get the original from Oliver Steele's funcitonal.js, as it doesn't have that problem.
-
Dave Nolan over 15 yearsWhy is it better to curry the higherup function rather than simply call it with some defaults?
-
Dave Nolan over 15 yearsThanks Hank - please can you expand on when it is useful in general?
-
gizmo over 15 yearsBecause it's highly more modular to be able to curry a "doMathOperation" with an addition/multiplication/square/modulus/other-calucation at wish than to imagine all the "default" that the higher function could support.
-
santiagobasulto over 12 yearsThis is great! I see it similar to the lisp quote that says "Lisp is a programmable programming language"
-
Prisoner ZERO over 12 yearsOkay...maybe I'm dumb...but I have no idea what you mean?
-
Nathan Long over 12 yearsInteresting, but this example doesn't appear to work.
offset+input
will beundefined + 1.60936
in yourmilesToKm
example; that results inNaN
. -
AngusC over 12 years@Nathan - offset can't be undefined - it defaults to 0
-
Roboprog about 12 yearsFrom what I've read (just now), "curry" is not normally part of a Function's bag of tricks, unless you are using the Prototype library or add it yourself. Very cool, though.
-
hacklikecrack over 11 yearsThe same can be acheived with ES5 bind() method. Bind creates a new function that when called calls the original function with the context of its first argument and with the subsequent sequence of arguments (preceding any passed to the new function). So you can do... var milesToKm = converter.bind(this, 'km',1.60936); or var farenheitToCelsius = converter.bind(this, 'degrees C',0.5556, -32); The first argument, the context, this, is irrelevant here so you could just pass undefined. Of course you would need to augment the base Function prototype with your own bind method for non ES5 fallback
-
Shawn almost 11 yearsCould you include the curry function you are using to make this example more complete?
-
Olivictor about 8 yearsI find this very expressive. Thanks!
-
Admin almost 8 yearsThis is really a bad answer. Currying has nothing to do with partial application. Currying enables function composition. Function composition enables function reuse. Reuse of functions increases code maintainability. It's that easy!
-
tom redfern almost 8 yearsWhen copying a pasting your answer wholesale from another website you must additionally wrap the copied bit in quotes. See stackoverflow.com/help/referencing
-
DR01D almost 7 yearsYour code is much easier to understand than Prisoner Zero's and it solves the same problem without currying or anything complex. You've got 2 thumbs up and he has almost 100. Go figure.
-
ZunTzu almost 6 yearsvar milesToKm = input => converter.curry('km', 1.60936, undefined, input) is a better solution. It is not sensitive to the parameter order. Currying is overrated in JavaScript.
-
Callat almost 6 years@ftor sir, you are a very bad answer. Currying is obviously about making functions more tasty. You clearly missed the point.
-
aestheticsData almost 3 yearsoftentimes it is said that currying is useful for function composition, but I can write a compose function without requiring currying if all my composed functions take one argument. Currying is needed when one or more of the functions take more than one argument.