The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination. Few media of creation are so flexible, so easy to polish and rework, so readily capable of realizing grand conceptual structures. —Fred Brooks
It’s not just flowery language. It’s the truth. Just consider what I was doing last weekend.
I was reworking some pet code for the sixth time. I hope it’ll to be ready for show in a few weeks. It will be the very first functional prototype of Wilde. I won’t describe the prototype now except to say that it is focused. It only implements one feature, and the interface is very primitive: a plain text editor. But that’s the idea. It’s a prototype. The most important thing is that it be easy to run. Something you wouldn’t need to download, something that runs in the browser (no, not an Applet), something HTML. So the real challenge (it’s taken far more effort than the prototype itself) has been the editor.
Browser, HTML, that means JavaScript. An expressive core that could use a little fleshing out. First thing off I take care of necessities like defclass and modclass so I can write:
defclass(function Circle(radius) {
this.radius = radius
},
function circumference() {
return Math.PI * this.radius * this.radius
}
)
Instead of the typo-prone:
function Circle(radius) {
this.radius = radius
}
Circle.prototype.circumference = function circumference() {
return Math.PI * this.radius * this.radius
}
Similarly, modclass allows you to add methods to existing classes. This lets me do the next important thing: Rubyize the Array class!
modclass(Array,
function all(block, options) { ... },
function any(block, options) { ... },
function collect(block, toArray, options) { ... },
function copy(toArray) { ... },
function detect(block, options) { ... },
function each(block, options) { ... },
function indexOf(el) { ... },
function inject(block, options) { ... },
function reject(block, options) { ... },
function select(block, options) { ... },
function remove(el) { ... },
...
)
Once I have the basics, it’s time to add Aspects to the mix. The more I learn about AOP, the more applicable it becomes. It’s just easier for me to think of adding features and behaviors to collections of objects than to figuring out some design pattern to get around OO limitations. For example, my Editor object handles turns keystrokes and mouse clicks into actions which it sends onto it’s associated Cursor. Simple, good OO design until I want to add a blinking behavior to the cursor. This behavior definitely belongs with the cursor, but it also depends on the editor. When you start typing or clicking (editor’s business), the cursor should stop blinking and become solid. Holy cross-cut Batman!
More to the point. I don’t want to think about blinking when I’m working on the other aspects of the cursor. I want it out of the way. Blinking is a little thing, but I have more complex behaviors and I want the freedom to mix and match ‘til my heart’s content. This means not only being able to weave in aspects, but being able to weave them out as well. As applied to blinking, the code looks something like this:
blinkingBehavior = Aspect.delayInstalling().modclass(Cursor,
function $init(editor) {
this.isBarVisible = true
this.isTyping = false
var that = this
function showBarNow() {
that.isTyping = true
that.showBar()
}
this.editorBlinkAspect = Aspect
.after(new Pointcut("onkeypress", editor), showBarNow)
.after(new Pointcut("onclick", editor), showBarNow)
this.blinkInterval = setInterval(close(this, this.blink), 500)
},
function hideBar() {
this.head.style.borderRightColor = INVISIBLE_COLOR
this.isBarVisible = false
},
function showBar() {
this.head.style.borderRightColor = VISIBLE_COLOR
this.isBarVisible = true
},
function blink() {
if (this.isTyping) {
this.showBar()
this.isTyping = false
} else {
if (this.isBarVisible) {
this.hideBar()
} else {
this.showBar()
}
}
},
function $uninstall() {
delete this.isBarVisible
delete this.isTyping
this.editorBlinkAspect.uninstall()
delete this.editorBlinkAspect
clearInterval(this.blinkInterval)
delete this.blinkInterval
}
)<br/>
// The behavior won't be available until you say:
blinkingBehavior.install()<br/>
// Then new cursor objects will blink. To turn blinking off for a particular cursor, you say:
blinkingBehavior.instanceUninstall(cursor)<br/>
// To disable blinking for all new cursors, you say:
blinkingBehavior.uninstall()<br/>
// That's it.
And that’s how programming is like poetry. Poetry differs from prose in that it experiments with the language. It twists it, turns it, flips it on its head just like I’m doing to JavaScript. Some languages are better for poetry than others. Of course, it all depends on what you call poetry. After all:
Reading Java programs is like reading poetry. Java is an epic, like the Odyssey. Writing a Java program is a ten year journey to find your way home. —Robert Cottrell
Commentary