The "Yes is No" Problem 12
<pre>
login: pdcawley
password: *****
Yes is no and no is yes.
Do you want to delete all your files? [y]: _</pre>
Rereading What’s wrong with Ruby, I realised that the meat of Matthew Huntbach’s worry about dynamic classes boils down to the ‘yes is no’ problem. Given just the source code of a Ruby program, it can be tricky to work out what the eventual program ‘looks like’.
Ruby source can be thought of as a program that you run on the Ruby kernel which creates a bunch of classes, objects and relationships between them. In static languages, once a class has been created, that’s it, the only way to change it is to recompile and restart the program. In dynamic languages, no class is every really ‘finished’, it can be modified at any time.
Done right, that can be a great thing. It’s instructive to look at the way ActiveRecord’s behaviour gets built up in Ruby. ActiveRecord::Base doesn’t really do all that much, it concerns itself with the nuts and bolts of marshalling data to and from the database and building the basic accessors and support methods needed to do that. Its implementations of basic methods like #create, #update and #save are positively naïve. New behaviours are then layered on by including other modules. Modules like ActiveRecord::Locking::Optimistic, ActiveRecord::Validations, ActiveRecord::Callbacks and others all layer behaviour on top of these methods, usually by renaming the old method, so #update becomes #update_without_lock and ActiveRecord::Locking::Optimistic#update_with_lock becomes the new #update, at least until ActiveRecord::Timestamp changes its name to #update_without_timestamps and installs the behaviour that it needs.
It can make the code harder to understand though. Say you’re looking at ActiveRecord::Timestamp#update_with_timestamps and you want to look at how #update_without_timestamps does its thing. If you simply grep for def update in the source code, you’ll find the implementation in ActiveRecord::Base and miss out on the alterations introduced by the locking module. You can’t simply look at the source code, you have to look at the order in which it was executed, and that can be a headache. Once you understand that you need to look at load order, and you find where that is defined, things get much easier, but things can get seriously scary when plugins come into play too.
What’s needed (or at least desirable) is a way of understanding what the state of the program and its classes will be at runtime. It’s easy in static languages, it’ll look like the source code says it looks. It looks like the source code says it will look in Ruby too, but Ruby source code can get a good deal more twisty. Even attr_accessor has the potential to blow minds…
Would you believe it’s a solved problem? It’s been solved for years too. Heck, the solution is older than David Heinemeier Hansson.
Counsel of Perfection
I’m thinking of Smalltalk, but I’m pretty sure that the Lisp Machine folks had similar tools at their disposal. In a Smalltalk image, you can browse the source code at any time, and it always reflects the current specification of the system. In a Smalltalk implementation of ActiveRecord, as soon as you rename #update to #updateWithouLocking that’s what it’s called. When, later you’re looking at the definition of #update and you want to know what #updateWithoutLocking looks like, you can browse straight too it. The knowledge that #updateWithoutLocking used to be called #update hasn’t been lost, you’ll find it in your changes, but it’s irrelevant to how the system behaves now.
I’ve been admiring Smalltalk from afar for a long time now, but until recently it wasn’t a language I spent any time programming in. I was more likely to think “How does Smalltalk do that?”, then fire up my local Squeak image and do a little fossicking about until I had a grasp on how things worked. Then I’d close it down and do something similar (or curse because I couldn’t) in Perl or Ruby. You can learn a great deal just doing that; if nothing else I’ve learned that I like Smalltalk’s message selector syntax a great deal.
Then, a few weeks ago, I watched a couple of screencasts of experienced Smalltalk developers doing their thing and… wow. I’ve always known that Smalltalkers rave about the environment, but I’d not really seen it in full flow.
So, I’ve been doing a few Code Katas in Squeak and starting to get a feel for working in the environment. It’s really, really lovely. Watch this space for more ‘coo er gosh’ type posts. Who know, maybe even an embarrassing screencast – if nothing else but so I can get some feedback from experienced Smalltalkers on what I’m doing wrong.
What about Ruby?
Short of someone coming up with a Smalltalk style interactive development environment for Ruby in the next half hour, this isn’t an immediately useful solution to the problem in Ruby.
But is it really a problem?
Having taken the time to explain why dynamic modification of classes can be a problem, I should point out that I don’t think it’s really a problem in practice. Sure, you can do Very Bad Things with it, but so what? You can do the same bad things (and more) in Smalltalk, if you couldn’t, it would be (nearly?) impossible to implement the Smalltalk environment in the first place. In the absence of an interactive programming environment, you need to take more care with structuring and organizing your code. Impose some standards on yourself – in Rails for instance, the various ActiveFoo classes are structured similarly: first there’s the ‘active_foo.rb’ file, which is responsible for loading up all the packages that define the class (generally found in the ‘active_foo’ sub directory) and includeing them in ActiveFoo::Base in the correct order. This convention helps comprehension. About the only thing I’d like to see further is some documentation in each module describing the behaviour they expect of any class they’re included in – behaviour like that of ActiveRecord::Validations is just too useful not to be pinched for other classes.
As always with these things, it boils down to the good taste and skill of the programmer. Write code that communicates your intent clearly to your fellow programmers – mere compilation is the least of your worries.
Updates
In the comments, Phil Toland has come up with links to some Smaltalk screencasts and a long presentation from Avi Bryant that includes some interaction with a Squeak image.
NB: For ‘ruby’, you can read ‘pretty much every dynamic OO language ever’. Except Matthew Huntbach didn’t write an article called “What’s wrong with pretty much every dynamic OO language ever”.
