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']
Tended to cheat in the implementation…

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
and in the False class
   ifTrue: trueBlock ifFalse: falseBlock
        ^falseBlock value
The sending the message value to a Smalltalk block is analogous to sending call to a Proc in ruby, although the actuall message varies with the arity of the block. In the case of ifTrue:ifFalse, the block arguments don’t really bedome block objects, they get compiled as in-line code. A lot of Smalltalkers ran across a head-scratcher when they got to the point of looking at the implementation of the value method in block which looks something like thi
   value
      "return the result of evaluating the receiver"
      ^self value
This certainly looks like it should be an infinite loop, but it isn’t. The trick is that in almost all circumstances, the Smalltalk compiler compiles sending value to a block into either special bytecodes. The only reason that the value method in block needs to be there is to handle cases like: aBlock perform:#value

where 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.

Posted in ,  | Tags , ,  | 2 comments | no trackbacks

Comments

  1. Alexander Kellett said 1 day later:
    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.
  2. Rick DeNatale said 5 days later:
    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.

Trackbacks

Use the following link to trackback from your own site:
http://talklikeaduck.denhaven2.com/articles/trackback/50

Comments are disabled