Friday, August 15, 2008

Regarding Ruby, instance_of?, kind_of?, and ===

So I was reading through some Ruby source code tonight -- primarily because I haven't in a while, thanks to a busy stretch of Java work -- and I ran across an idiom that I found a little confusing at first. The Ruby source code belongs to Why the Lucky Stiff's Shoes project, "a very informal graphics and windowing toolkit" (according to the official website).

One of the first files I looked at, lib/shoes.rb, opens right up with this interesting bit of interestingness:
class Range 
def rand
conv = (Integer === self.end && Integer === self.begin
? :to_i
: :to_f)
((Kernel.rand * (self.end - self.begin))
+ self.begin).send(conv)
end
end
Okay. So we're defining a rand() method on the built-in Ruby class Range, which will return a random value from within the begin and end values of the Range. Neato. And apparently the use of conv is meant to produce a result of a float or an integer, depending on the nature of the endpoints of the Range. Again: neato. But I hadn't seen the use of the === operator in this context before.

The docs and the Pickaxe book are a little obtuse on this, so I did some irb spelunking:
>> r = (1..27)
=> 1..27
>> r.class
=> Range
>> r.begin
=> 1
>> r.begin.instance_of? Integer
=> false
>> r.begin.class
=> Fixnum
>> r.begin.kind_of? Fixnum
=> true
>> r.begin.kind_of? Integer
=> true
Hmm. So instance_of() doesn't mean quite the same in Ruby as, say, the instanceof operator in Java does. (That, or Fixnum doesn't truly inherit from Integer in Ruby.)

Also, as it turns out:
>> Fixnum === r.begin
=> true
>> Integer === r.begin
=> true
So it would seem that the [class] === [value] syntax is syntactic sugar for [value].kind_of? [class]

It's the bit about:
>> r.begin.instance_of? Integer
=> false
that surprised me most, being the pathetic Java programmer that I am. And being that (according to the documentation for Integer on ruby-doc.org):
Integer is the basis for the two concrete classes that hold 
whole numbers, Bignum and Fixnum.
Apparently "is the basis for" != "is a superclass of".

Or is that "!==="...?