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?
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 undefined
s), 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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With