Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it me, or does John Resig's popular blog post on partial application not work?

John Resig has a popular blog post on partial application: http://ejohn.org/blog/partial-functions-in-javascript/ It's mentioned in many places, and has

However, the code in the blog post doesn't work. Here it is:

Function.prototype.partial = function(){
  var fn = this, args = Array.prototype.slice.call(arguments);
  return function(){
    var arg = 0;
    for ( var i = 0; i < args.length && arg < arguments.length; i++ )
      if ( args[i] === undefined )
        args[i] = arguments[arg++];
    return fn.apply(this, args);
  };
};

var delay = setTimeout.partial(undefined, 10);

delay(function(){
  alert( "A call to this function will be temporarily delayed." );
});

Now, if you try to run this in your console it'll work fine. However, if you try to use the delay function again, it won't work. Try to run this in your console after running the first segment:

delay(function(){
  alert( "This is a test" );
});

Note that the second message doesn't appear - the first message does. Of course, if you remake the delay function, it works, but why on earth would you want to remake your partially applied function every time you use it?

Now, given that this blog post is the second Google result for "partial application javascript" and seems to be fairly popular, I doubt that it's completely broken. On the other hand, tons of code samples and the popular Prototype.js library function in the way I'd expect - reusable partial application. His currying function, slightly higher up the page, works exactly the way I'd expect. Thus:

  • Is John Resig's code incorrect?
  • If it's not, why does his code force you to remake your partially applied function every time you want to use it?
like image 455
Owen Versteeg Avatar asked Apr 08 '14 23:04

Owen Versteeg


1 Answers

Is John Resig's code incorrect?

Yes. Even if you doubt it, it just is completely broken.

The partial application with undefined doesn't work more than once, because he's modifying the shared args array. And partial application without any undefined doesn't work at all.

It still might be helpful for generating callbacks or the like that are only called once, but that's not very functional.

Fixed:

Function.prototype.partial = function() {
  var fn = this, args = arguments;
  return function() { 
    var filledArgs = Array.prototype.slice.call(args);
    for (var i=0, arg=0; arg < arguments.length; i++)
      if (filledArgs[i] === undefined)
        filledArgs[i] = arguments[arg++];
    return fn.apply(this, filledArgs);
  };
};

Notice that unlike constraining the length of filledArgs to the number of partially given arguments (including undefineds), this now does accept arbitrarily many additional arguments and just appends them in a manner similar to bind.


Oh, and while we're at it, John Resig also got the term currying wrong, he is doing partial application with his .curry method as well (Wikipedia wasn't that accurate in 2008).

like image 193
Bergi Avatar answered Nov 07 '22 05:11

Bergi