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.

Show Comments