Bind vs Curry in JS

As of ECMAScript 1.8.5 javascript implemented the bind() function which can also be found in underscore.js as _.bind(). It allows you to bind a function to a this context as well as bind some predefined arguments. Binding predefined arguments in other functional languages like haskell is known as partial function application and goes by the name of currying.

So whats wrong with bind

The problem with bind is that it has two majorly different use cases, context binding and partial function application. This makes reading code that contains bind() calls in it slightly less concise because just reading the name of the function isn't enought to tell you what the intended goal of the operation is. It can be used for context binding, partial function application or both.

Sounds like a swiss army knife

Swiss army knife functions are something to be weary of when you intend to write concise and readable code. By looking at the funciton protocol for bind it seems like it was originally intended to be used just for context bind and partial function application was almost an afterthought. For this reason, when you use the bind function strictly for context binding not only does it create intuitive code but also cleaner looking code. You'll see what i mean when you try to use the bind function strictly for currying as it creates the opposite effect.

/**
 * `bind` used strictly for context binding
 */
var base = {  
    greeting: "Welcome",
    name:     "Joseph",
    greeter:  function() {
        console.log(this.greeting + " " + this.name);
    }
}

var asyncOp = function(callback) {  
    // do async work then invoke the callback
    callback();
}

// nice clean and intuitive use of `bind`
ascynOp(_.bind(base.greeter, base));

/**
 * `bind` used strictly for currying
 */
var lines   = function(text)       { return text.split("\n"); },  
    unlines = function(lines)      { return lines.join("\n"); },
    drop    = function(count, arr) { return arr.slice(count); };

var stripFirstLine = _.compose(  
    unlines,
    _.bind(drop, undefined, 1), // note the `undefined` 
    lines
);

var text = "line1\nline2\nline3";  
console.log(stripFirstLine(text));  

We can do better than that

So the undefined parameter in the previous example is a staple wtf code sighting in javascript. We can prevent having to pass undefined while making our code more concise by using a custom implementation of curry. You can see how the method we composed becomes more concise in the following example.

/**
 * Custom curry implementation. There are probably cleaner ways to 
 * implement `curry()` but this is just the one i use and it works
 * for me.
 */
// i like to create it as an underscore mixin
_.mixin({  
    curry: function(func) {
        var applied = Array.prototype.slice.call(arguments, 1);
        return function() {
            var args = applied.concat(Array.prototype.slice.call(arguments));
            return func.apply(this, args);
        };
    }
});

var lines   = function(text)       { return text.split("\n"); },  
    unlines = function(lines)      { return lines.join("\n"); },
    drop    = function(count, arr) { return arr.slice(count); };

var stripFirstLine = _.compose(unlines, _.curry(drop, 1), lines);  

Conclusion

  • Use bind strictly to bind functions to contexts
  • Use an implementation of curry instead of bind to create partially applied functions.