Ruby’s case statement uses ===

I’ve not found this stated clearly enough elsewhere so I’m doing so myself.

Ruby’s case statement calls the === method on the argument to each of the when statements

So, this example:

case my_number
  when 6883
    :prime
end

Will execute 6883 === my_number

This is all fine and dandy, because the === method on a Fixnum instance does what you’d expect in this scenario.

However, the === method on the Fixnum class does something different. It’s an alias of is_a?

That is cute, because it allows you to do this:

case my_number
  when Fixnum
    "Easy to memorize"
  when Bignum
    "Hard to memorize"
  end

But it won’t work as you might expect in this scenario:

my_type = Fixnum
case my_type
  when Fixnum
    "Fixed number"
end

This won’t work because Fixnum === Fixnum returns false because the Fixnum class is not an instance of Fixnum.

My workaround for this is to convert it to a string first. Not sure if that’s the best solution, but it works for me(tm).

my_type = Fixnum
case my_type.to_s
  when "Fixnum"
    "Fixed number"
end

Comments

Rahim says:

Any idea of the original rationale for the crazed implementation of Fixnum.=== ?

>> Fixnum === Fixnum
=> false

doesn’t seem like a good move to me!

john says:

Rahim, it’s possible that it’s optimized for case statement usage, which is pretty neat to be honest. Was just unexpected to me until I understood what was going on.

In fact, the docs suggest that:

http://www.ruby-doc.org/core/classes/Object.html#M000345

Rahim says:

=)

I read the docs and still couldn’t see when that choice would help for case statements (perhaps because I was biased by your example…)

Another, at first glance odd, result this leads to is eg:

>> Fixnum === 2
=> true
>> 2 === Fixnum
=> false

Rahim says:

I think misread your initial post, I thought you were saying your second example didn’t work as expected (which looking at it again looks like a pretty common pattern which made it all the more surprising).

Googling gives a few people who’ve discussed the same behavior (http://www.justskins.com/forums/doing-a-case-on-class-23663.html, http://blog.jayfields.com/2007/03/ruby-operator.html), both of whom quote the ri docs for Module#=== which explains my observations :

——————————————- Module#===
mod === obj => true or false
—————————————————–
Case Equality—Returns +true+ if _anObject_ is an instance of
_mod_ or one of _mod_’s descendents. Of limited use for modules,
but can be used in +case+ statements to classify objects by class.

Juan says:

Do this =>

my_type = Fixnum
case my_type
when Fixnum:
“Fixed number”
end

zoo says:

Nope, that returns nil.

Bug says:

The point of confusion is that ‘===’ is a poor mnemonic for the particular kind of binary relation that it represents. It gives the impression that it acts as an identity check or some other equivalence relation, with which Ruby’s conception of case checking is incompatible.

Bug says:

The point, then, is that the method ‘===’ is arbitrarily named, and might have been named more aptly. For example, ‘case_of?’ conveys that the object is being tested as belonging to a group of objects of which it is a special case. 2 is a special case of 2, just as it is a special case of Fixnum or Numeric, or even (1..7).

Thank you for this insight! After banging my head against why a case statement on my_object.class comparing class types wasn’t working, this post saved the day.

Leave a Reply to Bug Cancel reply