WTF? Case statement disfunctional?

Hi

I have a simple problem with the class of an object not being recognised
by the case statement. Am I just being an idiot again or is there
something more sinister going on here?

a=1
case a.class
when Fixnum
puts “It’s a number silly”
else
puts “Can’t figure it out”
end

This snippet of code returns “Can’t figure it out” which is just wrong?

a.is_a? Fixnum => true and a.class => fixnum and a.class == Fixnum =>
true, so why dont my case statement work?

case a.class.to_s
when “Fixnum”

does the trick, but it just offends my aesthetics.

Any ideas? Comments?

Regards

Pieter H.
South Africa

On Tue, Jul 6, 2010 at 6:21 AM, Pieter H. [email protected]
wrote:

else

Posted via http://www.ruby-forum.com/.

Change it to
a = 1
case a
when Fixnum
puts “It’s a number silly”
else
puts “Can’t figure it out”
end

Case statement turns into ===
and Fixnum === Fixnum is false, however, Fixnum === 1 is true

On Tuesday 06 July 2010, Pieter H. wrote:

| else
|…
|
|does the trick, but it just offends my aesthetics.
|
|Any ideas? Comments?
|
|Regards
|
|Pieter H.
|South Africa

In a case expression, to decide which of the branches should be
executed, ruby
calls the === method of the object after each when clause, passing as
argument
the object after the case clause: the branch which will be executed is
the
first one for which this method returns true.

What does this mean in your code? It means that ruby will call
Fixnum.===
passing it a.class, which means Fixnum. But Fixnum.===, which is the
same as
Class#=== since Fixnum is an instance of Class, returns true if the
argument
is an instance of Fixnum and false otherwise. Of course, Fixnum is not
an
instance of Fixnum, so that branch is not executed.

To achieve what you want, you need to write

case a

rather than

case a.class

This way, the object passed to Fixnum.=== will be a, which is indeed an
instance of Fixnum.

It is very easy to be confounded by this issue (it also happens to me
from
time to time).

I hope this helps

Stefano

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

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.

This is all a bit counter intuitive to me and !(least surprising).

Pieter

On 7/6/10, Pieter H. [email protected] wrote:

==> false
but,
Fixnum===a #=>true

=== is not symmetric with respect to its arguments. I forget what the
right word for that is.

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.

Don’t think of === as a ‘more forgiving’ ==. It doesn’t provide a
superset of =='s behavior; it’s more like a better equality operator
for use in case expressions. === actually has rather complicated
semantics which depend on the type of its left-side argument, so it’s
hard to come up with a pithy one-line description.

On Tuesday 06 July 2010, Pieter H. wrote:

|==> 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

Caleb C. wrote:

Don’t think of === as a ‘more forgiving’ ==. It doesn’t provide a
superset of =='s behavior; it’s more like a better equality operator
for use in case expressions. === actually has rather complicated
semantics which depend on the type of its left-side argument, so it’s
hard to come up with a pithy one-line description.

I think of “pattern === val” as a general pattern matching predicate,
which makes the asymmetry more palatable. It’s kind of unfortunate that
the “===” string itself is symmetrical.

2010/7/6 Caleb C. [email protected]:

On 7/6/10, Pieter H. [email protected] wrote:

but,
Fixnum===a #=>true

=== is not symmetric with respect to its arguments. I forget what the
right word for that is.

Maybe you meant “noncommutative”.

Kind regards

robert

Hi Guys

Once again thanks for your patience with me. I have been programming in
ruby for about 1.5 years now and would like to start thinking that I’m
not quite a newbie anymore, but then something like this ups and smacks
me on the chin. I’m on track again though and loving it.

Regards

Pieter

2010/7/6 Pieter H. [email protected]:

Once again thanks for your patience with me. I have been programming in
ruby for about 1.5 years now and would like to start thinking that I’m
not quite a newbie anymore, but then something like this ups and smacks
me on the chin. I’m on track again though and loving it.

Just making sure we do not overlook anything: you are aware of the
other form of case, are you?

irb(main):001:0> case
irb(main):002:1* when 1 > 2 then puts “silly”
irb(main):003:1> when 1 > 20 then puts “extraordinary silly”
irb(main):004:1> when 1 > 0 then puts “that’s just right”
irb(main):005:1> else
irb(main):006:1* puts “now what?”
irb(main):007:1> end
that’s just right
=> nil
irb(main):008:0>

Note, I am not saying that this would be more appropriate in your case

  • just trying to take care of your chin. :slight_smile:

Kind regards

robert