From: Stefano Crocco Date: 2010-07-06T22:12:04+09:00 Subject: Re: WTF? Case statement disfunctional? On Tuesday 06 July 2010, Pieter Hugo wrote: > |Hi guys > | > |Thanks for the prompt and helpful responses. Your proposals work, but I > |am still unhappy about spending an hour tracking this bug. > | > |So CASE uses '===' and not '==', am I correct? So why does > | > |a = 1 > |a === Fixnum > | > |==> false Because that uses 1 === Fixnum that is it calls the === method of 1, rather than the === method of Fixnum. Fixnum#=== works like Fixnum#== (note here we're speaking of the === instance method of Fixnum, as shown by the use of the # symbol, rather than of the === class method I spoke of in my other message and which is indicated using the . symbol), so 1 === Fixnum returns false because Fixnum is not a number and so can't be equal to 1. > |Furthermore, In my original example, one would expect a.class === Fixnum > |to return true in any case, as '===' is supposed to be more forgiving > |than '==', but it doesn't. You shouldn't think of === as being more forgiving than ==. According to the documentation of Object#===: Object#=== Case Equality--For class Object, effectively the same as calling #==, but typically overridden by descendents to provide meaningful semantics in case statements. This means that === is not a more forgiving version of ==. It's an operator which is written so that it makes things easier in a case statement. For example, take the class Regexp. Suppose I have a string str and I want to test which regexp matches it. I can write: case str when /a/ ... when /b/ ... when /c/ ... else ... end This can be done because Regexp#=== returns true or false depending on the value returned by its match? method. The same happens for Ranges. If n is an integer, I can write: case n when 1..3 ... when 5..6 ... else ... end Again, this works as I expect because Range#=== uses the include? method to determine whether to return true or false. For certain classes, instead, the more sensible behaviour for this method is to work like ==. This is what happens with Integer, String and Array, for example. Now, regarding your case. When you put a class in a When clause, what do you usally want to do? Check whether the object being tested is an instance of that class. So Class#=== returns true if the object is an instance of the class and false if it isn't. And since Fixnum is not an instance of Fixnum, you get false. > |This is all a bit counter intuitive to me and !(least surprising). Once learnt that case statements use the === operator (and I think most of the ruby books mention it when they speak of case), I had no issue with this. Indeed, I think it's a simple way to be able to use the case statement in a meaningful way also with custom classes (otherwise, you'd be limited to use it only with built-in classes, as in C, where you can't even use it with strings). I hope what I said clarified things at least a bit. Stefano