Yehuda Katz is a member of the Ember.js, Ruby on Rails and jQuery Core Teams; he spends his daytime hours at the startup he founded, Tilde Inc.. Yehuda is co-author of best-selling jQuery in Action and Rails 3 in Action. He spends most of his time hacking on open source—his main projects, like Thor, Handlebars and Janus—or traveling the world doing evangelism work. He can be found on Twitter as @wycats and on Github.

Emulating Smalltalk’s Conditionals in Ruby

If you follow my blog, you know that I enjoy emulating language features from other languages (like Python) in pure-Ruby. On the flight back from London, I read through Smalltalk Best Practice Patterns, and was reminded that Smalltalk doesn’t have built-in conditionals.

Instead, they use method calls (aka message sends) to do the heavy lifting:

anObject isNil
  ifTrue:  [ Transcript show: 'true'. ]
  ifFalse: [ Transcript show: 'false'. ].

In contrast, the typical way to do that in Ruby is:

if an_object.nil?
  puts "true"
else
  puts "false"
end

However, the Smalltalk approach is fully compatible with Ruby’s pure-OO approach (which is, in fact, inherited from Smalltalk in the first place). So why not be able to do:

(an_object.nil?).
  if_true  { puts "true" }.
  if_false { puts "false" }

In Ruby, unlike Smalltalk, message sends use a “.“, so we need to use periods between the message sends to link them together. With that exception, the semantics of the two languages are roughly identical. And, as usual, the implementation in Ruby is pretty straight-forward:

class Object
  def if_true
    yield
    self
  end
 
  def if_false
    self
  end
end
 
class NilClass
  def if_true
    self
  end
 
  def if_false
    yield
    self
  end
end
 
class FalseClass
  def if_true
    self
  end
 
  def if_false
    yield
    self
  end
end

In Ruby, false and nil are “falsy”, while all other values are “truthy”. Since everything in Ruby inherits from Object, we can simply define if_true and if_false on Object, FalseClass, and NilClass. If the block should be called (if_true on truthy values, or if_false on falsy values), we yield. In all cases, we return “self” so that we can chain if_true to if_false. And that’s it.

Of course, this means extending core classes, which some people are shy about doing, but it does mirror the semantics of Smalltalk.

8 Responses to “Emulating Smalltalk’s Conditionals in Ruby”

I’m definitely not a Ruby expert but the semantic of your Ruby version seems slightly different from the Smalltalk one.
ifTrue:ifFalse: is a single method in Smalltalk while you’re chaining methods in Ruby to get the behaviour.

It’s nice to see that Ruby unleashed Smalltalk’s style to a wide audience ;)

It’s close but true emulation would also retain return value semantics rather than result in self. The way the smalltalk code works is on a single message dispatch of ifTrue:ifFalse: with two arguments rather than two separate calls. Block syntax obviously makes it more readable, so in Ruby (1.9) if might look like this:

# Taking a shortcut on implementation by using the existing if impl.
# You could easily split it out into true and false cases but I am lazy.
class Object

def if(sel = {})
if self && sel[:true]
sel[:true].call
elsif !self && sel[:false]
sel[:false].call
end
end

end

(2 == 2).if true: -> { p true },
false: -> { p false }

The pointy lambdas help a lot in this case, though attempts to make bar {} blocks could be better if syntax complexities are ever solved.

Here’s a deep-dark secret.

Although Smalltalk control flow is defined as messages to booleans and blocks, most implementations don’t actually do it that way. They cheat and compile the code using testing and branching byte-codes.

http://talklikeaduck.denhaven2.com/2006/10/10/boolean-implementations-ruby-smalltak-and-self

If I recall correctly, one of Dave Ungar’s motivations in pursuing the Self language was to experiment with a Smalltalk-like language which didn’t use such tricks, but actually do everything as a message send, and then develop techniques to dynamically optimize at run-time. Work which led to things like JIT.

To emulate the ST behavior, the funtion should be .ifTrueifFalse({puts “true”}, {puts “false”}), as Francois said.

You guys are right. I like Brian’s solution. That said, I think that the Ruby version I provided is close enough to the underlying philosophy of the Smalltalk approach to be acceptable :)

A couple of months ago, I wrote something which emulates *Ruby’s* conditionals in Ruby: http://JoergWMittag.GitHub.Com/b001e/. (This was the first piece of Ruby code I ever published, so please ignore the horrible $LOAD_PATH and LoadError abuses.) And when I say “emulate Ruby”, I mean it: I actually ported the relevant RubySpecs to my syntax, and they all pass. I never got around to doing a comparative benchmark between my implementation and the builtin keywords/operators. On a well-designed Ruby implementation, the difference should be negligible, but I suspect that on an actual existing Ruby implementation mine will be significantly slower.

Some things in Smalltalk I wouldn’t want to emulate. The Smalltalk code looks more like a case statement to me:

case an_object.nil?
when true then puts “true”
when false then puts “false”
end

I did the exact opposite – started adding ruby-style constructs to Smalltalk. Never had the time to take it very far though: http://github.com/threehv/gstr

Leave a Reply

Archives

Categories

Meta