
Currying via ECMAScript 5 Binding
This article will exam function currying in JavaScript and introduce currying via native binding.
Currying
What is currying and why should I care? I do not even like Indian food. Well you obviously have no taste. I for one love Indian food, but that is not what we are talking about here. Currying is partial function invocation/application. If you like JavaScript then you will love currying. Below is the “Hello World” of currying in JavaScript, the add function. WARNING: This example was taken directly from here, but most examples use the same implementation more or less, so I decided to use a DRY approach and leverage the work of others.
var add = function (a,b) { return a + b; }; var addTen = add.curry(10); // create function that returns 10 + argument addTen(20); // 30 // curry is not a native function, so an implementation needs to be written function toArray(enum) { return Array.prototype.slice.call(enum); } 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(toArray(arguments))); }; }
I am not going to explain the benefits of currying in this article or do a comparative analysis to other methods of achieving the same results, but here is my opinion without any supporting arguments. Currying is good. It fits well with the design of the language, which has really strong functional tendencies with a sprinkle of OO. However, it seems like the people who are drafting ECMAScript 7 are determined to ruin this by adding classes and fattening up an elegantly simple language. If you want classical inheritance because it makes you feel warm and fuzzy then you can cut it yourself. That is the beauty of JavaScript. </rant>
Binding
Now that we know what currying is and my opinions on unrelated topics we need to understand binding before we can use it to curry. First do not confuse this with event binding. Binding creates a new function, which is bound to the function passed along with any optional paramaters; the signature is identical to function.call().
(function () { var type = 'unicorn', animal, getType, myGetType; animal = { type: 'bear-o-dactyl', getType: function () { console.log(this.type); } }; animal.getType(); // logs bear-o-dactyl getType = animal.getType; getType(); // logs unicorn, because 'this' refers to window myGetType = getType.bind(animal); myGetType(); // logs bear-o-dactyl, because 'this' is now animal }()); // don't believe me? Copy, paste, and execute in a debugger console
That is binding in a nutshell. It is very powerful, which brings us to the next section.
Currying by Way of Binding
Currying is a concept/pattern/insert correct label here. Implementation is left to the developer. There is not a native curry method, but JavaScript is flexible enough and powerful enough that it is not required. You can either cut your own or use bind as follows.
// Let's return to the add example var add, addTen; add = function (a, b) { return a + b; }; addTen = add.bind(undefined, 10); addTen(20); // returns 30 just like the first example
By simply passing undefined as the first parameter to bind it then partially applies the function invocation to the function being bound.
Conclusion
You no longer need to hang a new function off the Function.prototype or cut your own curry implementation. Bind can be used to implement currying – if your browser supports ES5; if your browser does not support ES5 then you are using the wrong browser.
Well.. my browser do not support ES5, but a nice illustration of currying though. And i love Indian food as well.
Legal Herbs
@briankkatz The you are using the wrong browser. :) There are some ES5 shims out there as well, https://github.com/kriskowal/es5-shim.
There are a few inaccuracies here. First, this comment:
// logs unicorn, because 'this' refers to window
has the right idea, but it is incorrect. That line logs "undefined" because 'type' is a local variable and there is no global 'type' variable.
Next, using .bind() to create a function with bound arguments is not quite the same as currying. Currying simply applies arguments to a function, but still leaves it as an ordinary function that can be called on any 'this'. That's what the Function.prototype.curry in your first snippet does.
.bind() creates a function that is forever after bound to a particular 'this'. No matter what you do with it after that, the 'this' will be fixed as the value you provided the first time you called .bind().
For more info:
http://stackoverflow.com/a/22128624/1945651