Boolean Implementations, Ruby, Smalltalk and self
Posted by Rick DeNatale Tue, 10 Oct 2006 16:43:00 GMT
The topic of defining a new ruby class which could have instances that, like false and nil were seen as a boolean which was not true, just came up on ruby-talk.
This has come up before, and it turns out that in Ruby being untrue is reserved to these two specific instances.
The boolean test is pretty deeply engrained into the implementation of ruby. The actual test seems to be defined in the RTEST macro in ruby.h#define Qnil ((VALUE)4)
#define RTEST(v) (((VALUE)(v) & ~Qnil) != 0)Which means that any object whose reference VALUE has any bit set other than the 3rd LSB is true.
In ruby, the control flow statements like ‘if’ aren’t messages, but are ‘compiled’ into a direct test and conditional branch.
Even Smalltalk, which defined even if/then/else as a message, e.g. booleanValue ifTrue:[Transcript show:'true'] ifFalse: [Transcript show:'false']In most Smalltalk implementations #ifTrue;ifFalse: and its ilk are never sent, but are, like in ruby, compiled into test and branch code. Some implementations might have had a fallback if booleanValue wasn’t actually a boolean, but IIRC most would trigger a MustBeBoolean exception.
By the way, those [...] are the Smalltalk analog to ruby’s blocks. In Smalltalk, blocks can be used as the value of any argument to a method, and a method could take more than one block argument.
But blocks are also another area where Smalltalk implementations tend to cheat a bit.
Smalltalk maintains the fiction that ifTrue:ifFalse: is really a message, and the methods in True and False are there to see:
in the True class ifTrue: trueBlock ifFalse; falseBlock
^trueBlock value ifTrue: trueBlock ifFalse: falseBlock
^falseBlock value value
"return the result of evaluating the receiver"
^self valuewhere perform is the analog to ruby’s send.
Smalltalk VM implementors like to say that it’s okay to cheat as long as they don’t get caught. MustBeBoolean is one area where they do get caught.
Pushing the Envelope
It’s quite a daunting task to fully implement a computation model where everything is a message and get reasonable performance. Smalltalk pushed this model quite far, but bowed to practicality in a few cases.Ruby, although it’s more dynamic than Smalltalk in a lot of ways, makes more concessions in it’s current implementation.
Dave Ungar’s self language, in the spirit of Nigel Tufnel, turned theknob up to eleven, and eschewed the pre-optimization of even if tests. Instead, the self implementation developed and used sophistacated run-time type inferencing and analysis to generate optimized code at runtime by detecting the common cases of a boolean receiver and converting the message send to tests and branches, while avoiding supporting the less common case.
In common with Ruby, self had a more dynamic object model than Smalltalk, based on delegation rather than inheritance. Something which engendered roaring debates in the early OOPSLA community over how delegation and inheritance are related. The status of that discussion ca. 1989 is captured in “The Treaty of Orlando.” which documented the observation that the differences were really a matter of perception and viewpoint.
The real contribution of self was setting the bar high in terms of the difficulty of implementing a simple and dynamic specification, and then jumping over it. The self team later applied the lessons they had learned to the implementation of Strongtalk, a Smalltalk VM which applied the techniques of the self implementation to both make Smalltalk more dynamic, and better performing.
Many of those sessons should be applicable to implementations of Ruby. I hope to see that unfold over time.










I found the Ruby community became so obsessed with creating arguments against those people that said it was slow that they can no longer conceive the possbility that the implementation could be massively improved on. For a while I worked on Rubydium (http://web.mac.com/lypanov/iWeb/Web/Rubydium.html). But was so demotivated by the lack of interest in the community (other than a tiny group) that I eventually decided to stop the work. Sad to say, but at the end of the day, I've come to the conclusion that Java is unfortunately now Good Enough, and far less buggy in general on top of that.
I look at things another way. Ruby performance seems to be good enough for quite a few purposes. The positive side is that there is a great deal of room for improvement. Matz has this on the roadmap as far as I can see, and I expect things will only get better either from the ruby-core team, or from alternate implementations. On the whole I find Ruby to be a much better language than Java, but such things are always personal opinions.