Inconceivable
The title is a reference to "The Princess Bride", and the phrase I wanted was "I don't think that word means what you think it means". I'm taking that from Neal Ford's rather confused post about meta-programming. His example is code generation, and he has a fairly deep misunderstanding of how Smalltalk works (and, quite probably, of how Ruby works as well) - let's have a look. He goes into an implementation of has_many in Ruby on Rails:
class Order < ActiveRecord::Base has_many :lineitems endFor those not familiar with Ruby, this is a method, defined as a class-level initializer (just like an instance code block in Java, a chunk of curly-brace code in the middle of a class definition, which Java picks up and executes as the class is instantiated). So, ultimately, this is the Ruby equivalent of a static method call, which gets called as the class is created.
He then goes into Smalltalk:
Let's talk about Smalltalk, which has first-class meta-programming. You could easily build has_many in Smalltalk, implemented as a button you click in the browser which launches a dialog with properties that allow you to set all the characteristics embodied in the Ruby version. When you are done with the dialog, it would go do exactly what Ruby does in Rails: generate a bunch of methods, add them to the class (stuff like the find_* and count_* methods). When you are done, all the methods would be there, as instance methods of your class.
OK, so at this point, the behavior is the same in Smalltalk as in Rails. But there is one key difference: The Smalltalk version using code generation. It's a sophisticated version of a code wizard, generating the code using meta-programming techniques. The Ruby version uses code synthesis: it generates the code at runtime, not build time. Building stuff at runtime means more flexibility. But that is a minor point compared to this one: In the Smalltalk version, you use the dialog and properties to generate all the methods you need.
I suppose a Smalltalker could do it that way, but it's not how it would normally be done. I've seen a lot of projects that do the kind of dynamic, runtime code generation he's talking about - but let me quote Alan Knight, who was talking about this in the IRC channel this morning:
aknight notes, for jarober's edification, that the GlorpActiveRecord stuff does have a hasMany method
and does no code generation at all from it
and yet, somehow it works
In the Glorp implementation of ActiveRecord (which will be part of our Seaside support, by the way), Glorp reads the database schema, figures out what's there, and then sets up the ability to read the appropriate database records without code generation.
I think a lot of people have strange ideas about how Smalltalk works, simply because the image throws them for a loop. Just because the image allows for some nifty development tools doesn't mean that everything done there is utterly alien.
Technorati Tags: ruby, meta-programming


Comments
OK, so tell us more!
[Glenn Vanderburg] September 6, 2007 11:15:49.337
Heh. I've been waiting for the Smalltalk community's reaction to that post. I had a nice talk with Neal while he was writing it, and it's true that he oversimplified some things. Additionally, one of the things we discussed was a straw-man idea for how to do an alternative version of has_many for Smalltalk that works by code synthesis rather than code generation. I know that kind of thing is possible in Smalltalk, but I've been told (by very experienced and widely respected Smalltalkers) that "Smalltalkers tend to stay away from such things because they don't work well with our tools."
So part of the issue is: yes, Neal and I know such things are possible in Smalltalk, but they're not nearly so widely used in your community as they are in Ruby, and we're trying to figure out why. (I'm open to the explanation that it's just a bad idea and we Rubyists haven't learned that yet, but I haven't seen a convincing argument in that direction.)
I'd love to know more about how Glorp's implementation works, and I'm certain Neal would as well. Please educate us!
confused
[isomer] September 6, 2007 11:44:40.354
I'm very confused by the post you quote, James.
I've been writing smalltalk code for about 15 years, and I have never written a code generator. The reason I and my fellow experienced smalltalk programmers don't have to generate code is precisely because smalltalk has good meta-level facilities. To be explicit, we can perform operations on message sends, classes and metaclasses at run time (because they are first-class objects), so there is no need to generate code at development or compile time.
[Alan Knight] September 6, 2007 11:48:43.343
Well, I'm not sure what you mean by "code synthesis" as opposed to code generation. The typical Smalltalk things would be either to generate code up front, because the point is to make the API's visible for people to use. So that would probably correspond to the case you describe. What you want there is that there are methods generated for findBySomethingOrOtherAboutItems:.
Or, a different alternative is just to do it entirely reflectively. So, for example, if I get sent a message findBySomethingOrOtherAboutItems I'd just pick it apart to discover that I'm looking at the #somethingOrOther attribute of the items collection.
But probably the most likely thing to do in Smalltalk is just to use the ability to pass blocks to describe the query. So, have one generic find: method that takes a block, and the user would write, e.g.
or for the hasMany case, something like In this case, at the point where we're referring to some attribute of an object, we'll go figure out that it corresponds to a database relationship and create and issue the query appropriately. This last is the way Glorp works (at 30,000 feet), although I did also provide pre-existing methods that are a little terser, if less expressive, for the simple cases. e.g. To find by primary key.[Glenn Vanderburg] September 6, 2007 12:36:10.953
Alan, thanks for the response. Except now I'm even more confused. I'll try to shed more light on the misunderstanding.
Sorry, those are the terms that Neal used in his blog. Code generation (as he uses the term) is an addition of code at the source level, where it's visible directly in source code or through the browser. Code synthesis is adding the code at runtime, each time the system is run, so that the additional boilerplate methods don't clutter up the source code that the developer has to maintain, and so that there's a clear, concise expression of intent. (And yes, I know that in Smalltalk "each time the system is run" is hard to pin down. That's part of the point. It's not to say that the Smalltalk way is bad, we're just trying to understand the way the two languages shape the way programmers do things.)
That's precisely what I thought, and what Neal's blog was saying. But James implied fairly strongly that we are wrong to think that, no?
You describe how the find methods work in Glorp, and that implementation sounds almost exactly like the one in Rails. There are a few simple methods inherited from ActiveRecord::Base, plus an implementation of method_missing (Ruby's version of doesNotUnderstand:) that dynamically does the more complicated stuff.
However, Neal's blog, and James', and my response were all asking in particular about has_many, which (in Rails) is an explicit declaration that adds methods to the class at runtime. The declaration "has_many :blivets" adds two methods: blivets (an accessor) and blivets= (a setter). Those methods do a lot of very nice things, which are mostly beside the point. From above:
That's what I'd love to know more about.
You haven't really explained a thing
[JT] September 6, 2007 13:59:30.973
You really have left me out in the dark here. You don't explain anything. We all hear about Smalltalk code generation and then you take a Seaside example and say most Smalltalkers don't generate code at runtime? How about some examples?
I think the original Ruby post is pretty accurate
[David Mitchell] September 6, 2007 16:15:03.150
Ruby and Smalltalk can both add methods at runtime. In Smalltalk, edit time is a kind of runtime. So all method definition (even done in a browser) is done at the editing runtime. In Ruby edit time is done in a text editor. There aren't any objects when editing. In Ruby, they write attr_accessor and that calls a method that creates the accessors at runtime. In Smalltalk, we *could* do that. But we don't. When we generate code, we usually do it during the editing runtime. If the target system is running (maybe we're writing the code as it runs in a debugger or as part of a test run) and we generate code, we have the intent to have that code be part of the source that goes under version control.
Manual Trackback
[Avi Bryant] September 6, 2007 16:47:01.714
http://www.avibryant.com/2007/09/code-generation.html
How to write attr_accessor in smalltalk
[Newbie] September 8, 2007 22:03:42.423
Okay, so how would you write the equivalent of attr_accessor in Smalltalk?
class Person attr_accessor :name def initialize(name) @name = name end end me = Person.new "Popeye" puts "My name is #{me.name}"Manual Trackback
[Jason Rogers] September 8, 2007 22:53:53.074
http://wordsanddeeds.org/blog/2007/09/08/meta-programming-in-smalltalk-vs-ruby/