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
This entry was posted in Ruby, Tech and tagged , , , , , , . Bookmark the permalink.

8 Responses to Ruby’s case statement uses ===

  1. 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!

  2. 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

  3. 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

  4. 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.

  5. Juan says:

    Do this =>

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

  6. 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).

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>