Function.prototype.curryCall()

Peter Michaux petermichaux at gmail.com
Tue Aug 28 14:20:59 PDT 2007


Hi,

Has JavaScript support for function currying been proposed? Maybe
there is already something like this planned?


Function.prototype.curryCall = function(scope) {
      var args = [];
      for (var i=1; i<arguments.length; i++) {
          args.push(arguments[i]);
      }
      var f = this;
      return function() {
          for (var i=0; i<arguments.length; i++) {
             args.push(arguments[i]);
           }
          return f.apply(scope, args);
      }
}


//------------
// Example use

function foo(a, b, c, d) {
   alert(this.name + a + b + c + d);
}

var bert = {name:'Bert'};

var curriedFoo = foo.curryCall(bert, 1, 2);

curriedFoo(3, 4); // alert says "Bert1234"




Currying is handy when attaching event handlers in a for loop since
for loops don't introduce a new scope with each iteration.

Suppose we want to attach a click handler to each item in an HTML
list. The handler prints the position of the item in the list and also
the innerHTML of the item. One way we could write this handler...

function handler(i) {
  alert('item ' + i + ': ' + this.innerHTML);
}

var items = document.getElementById('myList').getElementsByTagName('li');

// three ways to attach the handlers

//----------------------------------------
// OPTION 1:  inline currying (what a mess)

for (var i=0; i<items.length; i++) {
  var item = items[i];
  item.addEventListener('click', (function(item, i) {
     return function(){
             return handler.call(item, i);
            }
  })(item, i), false);
}


//----------------------------------------------
// OPTION 2: extracted currying (a little nicer)

function curryHandler(item, i) {
    return function() {
      return handler.call(item, i);
    };
}

for (var i=0; i<items.length; i++) {
  var item = items[i];
  item.addEventListener('click', curryHandler(item, i), false);
}


//--------------------------------------------------------
// OPTION 3: with Function.prototype.curryCall (succinct!)

for (var i=0; i<items.length; i++) {
  var item = items[i];
  item.addEventListener('click', handler.curryCall(item, i), false);
}

The event object will be passed as the second argument to the handler function.

There is precedence for other developers wanting this functionality. I
believe Prototype's Function.prototype.bind() may be the same thing as
the Function.prototype.curryCall() that I've written. The Prototype
Function.prototype.bindEventListener() will pass the event object as
the first argument to the curried function.

Any thoughts on the utility of currying as part of JavaScript?

Peter



More information about the Es4-discuss mailing list