Friday, May 30, 2008

Lightweight JavaScript Events

I've found it handy on a few projects to mix in minimalistic event support to certain JavaScript objects. The basic idea is that you listen (aka "subscribe") for some event on an object, and get notified by the object when it fires (aka "publishes") that event, along with any parameters it decides to attach. This is ridiculously easy to implement in JavaScript, yet it's a really powerful abstraction mechanism for UI programming. It's even easier when you use a library like Prototype to take advantage of its Enumerable and Function extensions. Here it is in under 20 lines of code:
EventManager = {
notify: function() {
if (this._listeners) {
var args = $A(arguments), event = args.shift();
this._listeners.each(function(listener) {
listener[event] && function() {
listener[event].apply(this, args);
}.defer();
}, this);
}
},

listen: function(listener) {
this._listeners = this._listeners || [];
this._listeners.push(listener);
}
};


To add event support to a class, just mixin the EventManager extension:
Foo = Class.create({
setX: function(x) {
this.x = x;
this.notify("setX", this.x);
}
});
Foo.addMethods(EventManager);


And then get down to it:
var object = new Foo();
object.listen({
setX: function(x) {
alert("x was initialized to " + x);
}
});
object.setX(1);


That's about it. Let me know if it comes in handy.

Labels:

Tuesday, May 20, 2008

Lisp, Agility, Readability

I recently had the opportunity to witness a curious dialogue on the mailing list for a local user group I participate in. The original poster, a public devotee and evangelist for Agile development practices, posted an announcement to the group for an upcoming Lisp workshop. A responder immediately threw down the gauntlet – How can you possibly be advocating Lisp as a development language, when Agile is so much about code readability? Doesn't Lisp encourage some of the most unreadable code on the planet? Doesn't Lisp stand for “Lots of Irritating Superfluous Parentheses?” Doesn't Lisp ... ?

I thought about this challenge for a minute, and had an immediate sense of both the rightness and wrongness of it. After all, haven't we all beaten our heads against the wall for days trying to debug some Rails code, only to realize that the answer was buried deep in some “magic” meta-programming hook? Or spent the afternoon hunting down the documentation for some obscure method that's throwing an annoying runtime exception, only to discover that our framework kindly generated it for us at compile time based on some esoteric configuration parameter? Lisp programs seem to represent the pinnacle of this kind of self-generating, self-manipulating metacode, and they can be damned hard to wrap your head around.

At the same time, isn't it hard to beat that sublime feeling of joy you experience when you write that minimal amount of declarative code, and everything Just Works? When you can alter some piece of metacode at the global level to effect an entire architectural change, without touching a single line of code throughout your entire application? Surely there is something deeply right about these kinds of abstractions that allow us to communicate at the level of meaning, as opposed to mechanics – to separate the why of a piece of software from the how.

It struck me then that we operate under two very different senses of the word “readability,” often slipping and sliding back and forth between the two without realizing it. To caricature them down to their essence, allow me to call them mechanical readability, and semantic or intentional readability. Mechanical readability is achieved when a piece of code clearly communicates what it does, whereas semantic or intentional readability is achieved when a piece of code clearly communicates what it means. And there's the rub – clearly these two concerns are constraining us constantly, tugging at each other from opposite directions, nearly always forcing us to sacrifice one for the other in any given instance. What's the answer? Can they be harmonized into some greater concern that preserves them both?

I don't think they can be harmonized exactly, but I do think there is a bit of sound earned wisdom that applies here – a jewel in the crown of the software developer's craft. Without trying to be too scientific or dogmatic about it, it might go something like this:
  • When addressing lower layers of abstraction, strive for mechanical readability.
  • When addressing higher layers of abstraction, strive for semantic readability.
  • Within a given layer of abstraction, strive to decouple the mechanical and intentional concerns as much as possible.
So where does that leave us as far as Lisp's alleged agility? Well, in truth, Lisp actually does address mechanical concerns far better than you might think. But its ability to express the semantic level of software is where it clearly shines. The temptation is to become obsessed with the semantic approach, to the point of obfuscation of the plain, simple mechanical tasks your software needs to perform. This is possibly the greatest temptation programmers face when given powerful meta-programming facilities. There's nothing quite so frustrating as having to work within forced abstractions.

However, don't assume a more “straightforward” mechanical style will clarify your code either. The reason your DSL doesn't flow well enough, or map onto your domain well enough, might not be that it's too abstract. It might just be the wrong abstraction. The semantic realm is a very conceptual, philosophical sort of beast. You won't write better abstractions by learning more about technological wizardry, but by practicing the dark art of simple, clear thinking.

Ultimately, it's the right ideas that set really great software apart.

Labels: , ,

Thursday, May 15, 2008

JS Library Roundup

In Part III of the JavaScript Renaissance, we spent another long evening exploring Prototype, Script.aculo.us, and jQuery, and getting a feel for the scope and power of these popular DHTML/Ajax libraries. A good chunk of that time was spent hacking at the Firebug console and experimenting with live HTML documents for immediate feedback.

You can download the presentation here.

Thanks again to the Wheaton ex-Perl Mongers for coming out to learn a little more JavaScript. Come out and see the Perl Mongers reborn as the first arm of the new Polyglot Programmers group!

Addendum: Some of you at the presentation wanted to know to use Greasemonkey to import libraries into web pages after they've already been loaded. Here's the skinny:
  • Go to https://addons.mozilla.org/en-US/firefox/addon/748 and add the Greasemonkey Firefox extension
  • After you restart Firefox you should see the telltale monkey face icon down in the lower right corner of your browser window
  • Right click on the monkey face and select "New User Script..."
  • Give the new script an identifiable name like "load_prototype" or "load_jquery", a namespace (can be anything, I'm not entirely sure what it's even used for), and a list of URLs to include or exclude from running the script
  • Configure a text editor to edit Greasemonkey scripts
  • Finally, enter and save the script

Here's the script I used to load prototype and most of script.aculo.us. Obviously, you'll need to change the paths to point to the files where they're installed on your system. Paste this into your new Greasemonkey document below the comments:

window.addEventListener('load', function() {
var head = document.getElementsByTagName('head')[0];
var libs =
[
'file:///..../prototype.js',
'file:///..../effects.js',
'file:///..../dragdrop.js',
'file:///..../builder.js'
];
for (var i in libs) {
var script = document.createElement('script');
script.type = "text/javascript";
script.src = libs[i];
head.appendChild(script);
}
}, false);


That should be enough to get you started! Please hit me up if you have any questions.

Labels: ,