Currying via ECMAScript 5 Binding

Blog
The best minds from Teradata, our partners, and customers blog about whatever takes their fancy.
Teradata Employee

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.

3 Comments
Well.. my browser do not support ES5, but a nice illustration of currying though. And i love Indian food as well.

Legal Herbs
Teradata Employee
@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