JavaScript curry: what are the practical applications?
@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" );
Javascript: Use cases of currying
Its usefull when doing partial application or composing. You can do something like this for example
const arr = [1,2,3,4]
const curry = (fn, ...a) => fn.length > a.length ? (...b) => curry(fn,...a.concat(b)) : fn.apply(null,a)
const add = curry((a,b) => a+b)
console.log(arr.map(add(5)))
const compose = (...fns) => fns.reduce((f,g) => b => f(g(b)))
console.log(arr.map(compose(add(5),add(2))))
What are real use cases of currying?
Real use case of currying is partial application.
Currying by itself is not terribly interesting. What's interesting is if your programming language supports currying by default, as is the case in F# or Haskell.
You can define higher order functions for currying and partial application in any language that supports first class functions, but it's a far cry from the flexibility you get when every function you get is curried, and thus partially applicable without you having to do anything.
So if you see people conflating currying and partial application, that's because of how closely those concepts are tied there - since currying is ubiquitous, you don't really need other forms of partial application than applying curried functions to consecutive arguments.
What are the practical advantages of currying?
First, it's very common to mistake partial function application for currying. See this for example (I'm sure there are better resources describing it, but this was the first one i found). I've almost never seen anyone use currying in practice (except for languages like Haskell, where every function is curried by the language itself, so to speak, but even that is in order to enable simple partial function application). Partial function application on the other hand is quite useful in many languages.
Anyway, assuming you're talking about partial function application (since that's what most people are talking about when they're asking about currying), the concept is not quite as natural in C++ as in a (purely) functional language, such as Haskell for example.
For example, here we define a function sum
that takes an array of numbers list
and sums all the numbers together. If you're unfamilir with the concept of fold (or reduce or inject, as it is sometimes called), read this. Anyway, it would look like this:
sum list = foldl (+) 0 list
But wait a minute. We could shorten it by using partial function application! Instead of supplying an argument, we just say that sum
is a function that is equal to foldl, with + and 0 partially applied.sum = foldl (+) 0
Which one is easier to read? A matter of preference probably, but the latter emphazises the relation between sum and foldl more clearly in my opinion. And please take into account that this is a very simple example. I honestly don't know how to write a good example in C++, so you'll have to excuse me there. In any case, what is the practical advantage? Readability. Clearer intent. Shorter code.Disclaimer: If you actually wanted to know the advantages of currying (as opposed to partial function application) I'm sorry to have made you read all this. But on the other hand, if you understand the difference between the two will also understand that currying is a great way to implement partial function application.
What is 'Currying'?
Currying is when you break down a function that takes multiple arguments into a series of functions that each take only one argument. Here's an example in JavaScript:
function add (a, b) {
return a + b;
}
add(3, 4); // returns 7
This is a function that takes two arguments, a and b, and returns their sum. We will now curry this function:function add (a) {
return function (b) {
return a + b;
}
}
This is a function that takes one argument, a
, and returns a function that takes another argument, b
, and that function returns their sum.add(3)(4); // returns 7
var add3 = add(3); // returns a function
add3(4); // returns 7
- The first statement returns 7, like the
add(3, 4)
statement. - The second statement defines a new function called
add3
that will
add 3 to its argument. (This is what some may call a closure.) - The third statement uses the
add3
operation to add 3 to 4, again
producing 7 as a result.
What is the difference between currying and partial application?
Currying is converting a single function of n arguments into n functions with a single argument each. Given the following function:
function f(x,y,z) { z(x(y));}
When curried, becomes:function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }
In order to get the full application of f(x,y,z), you need to do this:f(x)(y)(z);
Many functional languages let you write f x y z
. If you only call f x y
or f(x)(y) then you get a partially-applied function—the return value is a closure of lambda(z){z(x(y))}
with passed-in the values of x and y to f(x,y)
. One way to use partial application is to define functions as partial applications of generalized functions, like fold:
function fold(combineFunction, accumulator, list) {/* ... */}
function sum = curry(fold)(lambda(accum,e){e+accum}))(0);
function length = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);
/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list) //returns 10
Currying: practical implications
First of all, I would like to stress, that due to compiler optimizations the two functions above will be compiled into the same assembly code. Without the optimizations, the cost of currying would be too high, i.e., an application of a curried function would require allocating an amount of closures equal to the number of arguments.
In practice, curried function is useful, to define partial application. For example, cf.,
let double = foo 2
let double2 x = foo2 (2,x)
Another implication is that in a curried form, you do not need to allocate temporary tuples for the arguments, like in the example above, the function double2
will create an unnecessary tuple (2,x)
every time it is called. Finally, the curried form, actually simplifies reasoning about functions, as now, instead of having N
families of N
-ary functions, we have only unary functions. That allows, to type functions equally, for example, type 'a -> 'b
is applicable to any function, e.g., int -> int
, int -> int -> int
, etc. Without currying, we would be required to add a number arguments into the type of a function, with all negative consequences.
Functional programming when to curry
There are different schools of thought on this, one is not necessarily more "functional" than the others. But first:
Libraries like ramda have a curry function that supports both syntaxes:
const add = R.curryN(2, (x, y) => x + y);
add(2)(3); // 5
add(2, 3); // 5
So don't get too hung up on the difference.There's a fairly important idiom that you can't really do while currying in JavaScript: varargs. I mean, you might be able to find a (laborious, convoluted) way to make it work, but generally you wouldn't. So if a function can take any number of arguments, it shouldn't be curried.
The next concern is performance.
That's a lot of extra function calls compared to e.g.
const add = (x, y) => x + y;
const add3 = add.bind(null, 3);
With the first version, every call to add
pays the price. With this one only the partially applied version does, and it uses a built in (likely faster) to boot.Finally, a word about contracts:
When you expose functionality to anyone other than yourself (co-workers, customers, open source users), you are making a contract with them that this is the way your function works. If you find that you need to change it later, there are changes that break your existing callers and ones that don't. No one cares about the latter, everyone will curse you for the former.
Going from uncurried to curried (via e.g. ramda) is a non-breaking change. Your existing callers will still work. The same is not true of the reverse.
Given all that, I would probably not curry any functions by default unless I'm very very sure that I will be calling them piecemeal every time. Remember, your callers (even if that's you from a different module) can always curry it themselves!
Related Topics
How to Handle Oncut, Oncopy, and Onpaste in Jquery
How to Compare 2 Functions in JavaScript
Can't Set Innerhtml on Tbody in Ie
How Do Print the Console Output of the Page in Puppeter as It Would Appear in the Browser
How to Auto-Slide the Window Out from Behind Keyboard When Textinput Has Focus
Handling Key-Press Events (F1-F12) Using JavaScript and Jquery, Cross-Browser
Cancel/Kill Window.Settimeout() Before It Happens
Why Is Window (And Unsafewindow) Not the Same from a Userscript as from a <Script> Tag
Do Websockets Allow for P2P (Browser to Browser) Communication
Waiting for Image to Load in JavaScript
How to Execute a Dynamically Loaded JavaScript Block
Angularjs Does Not Send Hidden Field Value
D3.JSON Method Doesn't Return My Data Array
Access 'Data-' Attribute Without Jquery
How to Limit Concurrency When Using Es6's Promise.All()