Sunday, February 17, 2008

Function Arity and JavaScript

A commenter on my last post asked about variable arity methods in JavaScript. Arity, for those unfamiliar with that term, simply refers to the number of parameters a function takes. In dynamic languages, however, arity is a more, well, dynamic concept, since a function can often be called with any number of parameters, or even with a collection to be taken as the argument list. So there are actually two different things going on, which I'll call invocation arity and declaration arity.

There's not much to invocation arity in JavaScript; you've probably seen it hundreds of times before. To determine the arity of invocation from within a method, just use the length property of the array-like arguments object:
SomeType.prototype.foo = function () {
if (arguments.length > 2) {
// do something requiring > 2 arguments
}
}

Declaration arity is similar: I can ask for the length property of the function itself. So for example, if I was passed a function into some higher-order operation, I might check the function's arity to determine how to invoke it:
function foreach(array, callback) {
for (var i = 0; i < array.length; ++i) {
if (callback.length === 1) {
// yield the element
callback(array[i]);
} else {
// yield the index and the element
callback(i, array[i]);
}
}
}

Beware declaration arity though: if your function is trying to be smart about its own arity by foregoing formal parameters and just using its arguments object, it will advertise its arity as zero:
function sum() {
var s = 0;
for (var i = 0; i < arguments.length; ++i) {
s += arguments[i];
}
return s;
}

print(sum.length); // prints 0

Labels:

Thursday, February 14, 2008

More Mongering

The Perl Mongers were at it again on Tuesday night, going above and beyond the call of duty, slogging through snow and slush to listen to more JavaScript tidbits. This time, we explored how to use JavaScript to interact with the browser window and HTML document, monitor and handle user events, and make remote HTTP calls. We finished up with some lively discussion questions about JavaScript libraries and frameworks and how they compete, relate, and stack on top of one another; so I'll try to go more into that question in the near future.

But for now, you can find the slides and samples of the presentation here.

Labels: ,

Eye Candy

As good fate would have it, I happened to stumble upon two really good Microsoft fonts yesterday -- Corbel and Consolas. Corbel is a clean proportional-width font, similar in some ways to Trebuchet, but a little more professional looking in my opinion. (The loopiness of Trebuchet is a little annoying, and the capital M always bothered me. Don't ask.) Consolas is a fixed-width font, almost entirely sans-serif like Lucida Console, except for a more Courier-looking lowercase g, i, and l. I've been satisfied with Lucida Console for programming, but it just falls a little bit short of great. But I think I've finally found a worthy successor.

By the way, don't even bother trying to run these without ClearType. You have been warned.

Unfortunately, these fonts aren't "free" (as in freedom), so I can't just post them for you to download. But they do come as a font pack for Visual Studio 2005 (if you have it installed) -- or for a lower-tech option, a little birdie told me that they come bundled with PowerPoint Viewer 2007.

Labels: , ,

Friday, February 1, 2008

Currying in Prototype

If you've spent much time in the functional or higher-order programming mindset, you've already gotten a feel for the expressive power that comes from the ability to compose new functions out of existing ones. In pure functional languages, one of the basic types of function composition operations is called "currying" (after Haskell Curry, an early pioneer in this particular space). When you curry a function, you essentially bind parameter values to it, and return a new function over the remaining parameters. Some languages have built-in support for currying as a fundamental operation (ML and Haskell, for example), but you can implement them in any language that supports closures and functions as first-class objects. The Prototype library, as of version 1.6, does this for JavaScript via the Function.prototype.curry method.

Here it is, in all its beautiful compactness (without the Object.extend confusion):
Function.prototype.curry = function() {
if (!arguments.length) return this;
var __method = this, args = $A(arguments);
return function() {
return __method.apply(this, args.concat($A(arguments)));
}
};
We'll walk through line by line:
if (!arguments.length) return this;
If no arguments were bound, simply return the function as-is.
var __method = this, args = $A(arguments);
Store the original function in a closure as __method, and the arguments to be bound as args.
return function() { ... }
Nothing surprising here. We're going to return a new function.
return __method.apply(this, args.concat($A(arguments)));
This is where the magic happens. The newly created function, when called, is going to return the result of calling the original function -- __method.apply(this, ...) -- with the original arguments -- args -- prepended to the invocation arguments.

An illustration is worth a thousand words:
function original(x, y, z) {
return x+y+z;
}

var bound_x = original.curry(10);
bound_x(20, 30) // ==> original(10, 20, 30)
// ==> 60

// sure, you can curry a curried function
var bound_x_y = bound_x.curry(20);
bound_x_y(30) // ==> bound_x(20, 30)
// ==> original(10, 20, 30)
// ==> 60

var bound_x = original.bind(this, 10);
var bound_x_y = bound_x.bind(this, 20);

Labels: