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.