« Moving | Main | Code as Screenplay »

September 06, 2007

Code generation in Smalltalk and Ruby

Neal Ford had a recent post about the difference between code-generation (he calls it "meta-programming", but that's an overloaded and ambiguous term) in Ruby and Smalltalk. The core of his point is this: in Ruby, code generation is done at runtime, which means that what gets checked into your source code repository is a high level statement like "has_many :foo", which then generates the code when it is executed. In Smalltalk, code generation is done at development time (triggered by some custom wizard-like extension to the IDE), and so the generated code itself is checked in and the intent, according to Neal, is lost (as a trade-off for other benefits, like the ability to take the generated code into consideration when doing refactorings and so on, whereas in Ruby that code is invisible to any static analysis).

This is a straw man: Smalltalkers understand the need to capture (and later modify) the intent as well as anyone else does. The solution is to make the generated code round-trippable. If you look at any real Smalltalk tools that generate code based on a custom tool (the SmaCC parser generator is a good example), it will preserve the settings from that tool, for example in a class comment, and the tool will let you inspect the intent, modify the intent, and regenerate the code.

To be concrete: any self-respecting Smalltalk tool that let you generate all the code associated with a "has_many" expression would annotate those methods with the "has_many" intent, in a way that the tools could understand, present to the user, and modify.

(James Robertson points out that ORM tools in Smalltalk tend not to use code generation anyway, but I don't think that really answers Neal's point.)

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00e0098be7b3883300e54ed8d27f8833

Listed below are links to weblogs that reference Code generation in Smalltalk and Ruby:

Comments

In other words, Ruby (on rails?) is opinionated, and doesn't want you to muddle with its opinion of how code should be generated.

One of the simplest DSL-ish tools in Smalltalk creates accessors for instance variable. Even this tool does not leave any trace of the intent, and cannot be "undone" or "changed" without editing the pair of generated methods. So while it may be possibly to do it in Smalltalk (e.g. do it all in class-side methods that will thus stick around), it is not something that Smalltalk provides any real support for (which class side methods should I use? how do I know which class-side methods of someone else's classes I should browse to understand their intent? How do I reverse or change that intent without editing the generated code?).

I think it could be built, but I don't believe it is there.

Bill, I disagree that generating accessors leaves no trace of the intent. Any person or tool can look at "someIvar ^ someIvar" and see clearly that it's a getter (in fact, in many Smalltalks, there's an optimized bytecode used just for accessors). I would say that recording the intent only becomes important for more complex examples - and again I point to SmaCC as a demonstration of how things should be done here.

I'm sure complex examples are good, but I think this simple one makes my point:

Ruby:
attr_accessor :foo

Smalltalk:
menu select "create accessors" for #foo
Henceforth, see 2 methods #foo and #foo: (whose bodies could be quite complex e.g. db access)

Contrast the resulting situation in each environment when just reading the source / browsing the class. Ruby directly reflects my intent, while ST reflects the result of implementing my intent.

Next try making a change e.g. change instance variable name #foo to #bar. Even using the refactoring browser you will still end up with foo and foo: lying around until manually cleaned up in Smalltalk. Likewise for an "Undo".

ST does not retain the intent in a useful way for this scenario. Perhaps SmaCC does better, but adding GUI tools to ST and making them faithfully retain intent is a *lot* harder than writing and using a meta-programming method in Ruby.

Now if there were an established and standard way of recording "attr_accessor" on the class-side, and the various tools (browsers, monticello, ...) behaved consistent with that std way, that would be something else.

Interesting discussion. I like the ST approach, but am often bitten by a related problem: how to rewind / re-initialize when my "meta-programming" methods change e.g. I change my implementation of "attr_accessor".

Avi, any best practice tips for this?

Steve, one simple best practice I've seen is to put all generated methods into a "generated" category and then wipe that category every time you make a change to the generation code. A long time ago (when I was fresh from Ruby and missing attr_accessor) I released a package called MetaMonkey which would do this kind of thing automatically, but I don't think I have a copy anymore.

Makes sense. Where do you centralize the (meta)method invocations that you will need to regenerate those? #initialize on the class side?

I suspect Monticello has to deal with all this stuff in a very systematic way as well.

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been posted. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment

My Photo

Twitter Updates

    follow me on Twitter