Monday, June 27, 2005

Compiling Away Does Not Understand

I was talking to Micheal Lucas-Smith, while we were at the Magic Kingdom, and I were talking about our designs in the contest. I mentioned that my study of prototypical languages had warped my design. He asked how that was and I told him that my survey response objects forwarded their calls to their corresponding questions if they didn't understand them. For example, a message send of #name to the response would forward the call to its question object. These messages have the form:
    ^self question name

Now, I'm a lazy developer. I didn't want to write these messages everytime I add some new messages to question that I want my response to asnwer. So, what do I do? I could implement #doesNotUnderstand so that it forwards the message, but I didn't want to do that because:
  1. It's hard to debug

  2. It's S-S-L-L-O-O-W-W

  3. Confusing

So, what do you do? Well, in Smalltalk, I can have the #doesNotUnderstand: message basically compile a new method for me because it's always of the form listed above. This gives me the performance and it's easy to debug. If you put these special methods in their own category then you can easily track them. Tracking them is good to catch subtle forwarding bugs. Here's what the #doesNotUnderstand: message looks like:
    doesNotUnderstand: aMessage
    (self definition respondsTo: aMessage selector)
    [self compile: aMessage selector forwarder: #definition.
    ^aMessage sendTo: self].
    ^super doesNotUnderstand: aMessage

And here's what the compile code looks like:
    compile: selector forwarder: forwarderSelector
    | messageStream header |
    messageStream := String new writeStream.
    header := (self perform: forwarderSelector) class methodHeaderFor: selector.
    nextPutAll: header;
    cr; tab;
    nextPutAll: '^self ';
    nextPutAll: forwarderSelector;
    nextPut: Character space;
    nextPutAll: header.
    self class compile: messageStream contents classified: self class autoGeneratedCategory

And that's it! This allows me to have a form of delegation by forwarding messages to certain objects. Other objects now do not have to grab an object just to perform actions on it. This makes your objects more shy which is a good thing. Micheal liked the idea and asked me why I had not blogged about it. I told him I didn't think much about it. It's just so simple. He told me that I should blog about it. Well, here it is...=)


Benoit St-Jean said...

Isn't it similar to the auto accessor feature that Squeak used to have (or still has?!)

Blaine said...

Yes, it is similiar in that you are lazyly letting the system define the dumb methods for you. Let the computer do the easy stuff. And it's that reason why I never blogged about it before.

zenspider said...

I often have my code generate code for me, especially when I'm in exploratory mode. Granted in ruby-land I don't have as easy a time getting said generated code back into my files, but it isn't so hard that I don't generate the code in the first place.

Blaine said...

I started generating code in Ruby and using macros in Lisp. The idea was planted with both and culminated when I was doesNotUnderstand: as a language extension mechanism. Someone complained that it was hard to debug and I thought, well, why don't just generate it? I firmly believe that you should let the computer write the easy code whenever possible. It excels at mundane work. Why not let it write my boring code? I got bigger fish to fry!