Why does “YYY” get printed out here?
class XXX
end
x = XXX.new
case x.class
when XXX
puts “XXX”
else
puts “YYY” # surprise!
end
Why does “YYY” get printed out here?
class XXX
end
x = XXX.new
case x.class
when XXX
puts “XXX”
else
puts “YYY” # surprise!
end
On Fri, Aug 20, 2010 at 10:51 PM, Ralph S. [email protected]
wrote:
when XXX
puts “XXX”
else
puts “YYY” # surprise!
end
It’s because of the definition of “===” which is used in the case
statement.
Does the following help?
class XXX
end
x = XXX.new
case x.class
when XXX then puts “XXX”
else puts “YYY” # surprise!
end #=> YYY
case x
when XXX then puts “XXX 2”
else puts “YYY 2”
end #=> XXX 2
case x.class
when Class then puts “XXX 3”
else puts “YYY 3”
end #=> XXX 3
Colin,
Friday, August 20, 2010, 4:29:37 PM, you wrote:
CB> On Fri, Aug 20, 2010 at 10:51 PM, Ralph S. [email protected]
wrote:
Why does “YYY” get printed out here?
class XXX
end
x = XXX.new
case x.class
when XXX
puts “XXX”
else
puts “YYY” # surprise!
end
CB> It’s because of the definition of “===” which is used in the case
statement.
CB> Does the following help?
CB> class XXX
CB> end
CB> x = XXX.new
CB> case x.class
CB> when XXX then puts “XXX”
CB> else puts “YYY” # surprise!
end #=>> YYY
CB> case x
CB> when XXX then puts “XXX 2”
CB> else puts “YYY 2”
end #=>> XXX 2
CB> case x.class
CB> when Class then puts “XXX 3”
CB> else puts “YYY 3”
end #=>> XXX 3
Colin, thank you. Very clear.
Now it’s my turn to complain about the unexpected behavior.
Having read your explanation and Dave T.’ Programming Ruby 1.9
book about the use of === in comparisons in case statements …
Is it he fact that case uses == (two ='s) for non-Class when’s and
uses === (three ='s) for when’s that are Classes?
It just seems to me to be an unnecessary surprise to do things
differently for objects of class Class.
Indeed
x.class == XXX # => true
X.class is XXX … and yet the when does not pick it up because ===
is being used for comparison.
Anyway, thank for your lucid and well-written response.
On Sat, Aug 21, 2010 at 12:13 AM, Ralph S. [email protected]
wrote:
Having read your explanation and Dave T.’ Programming Ruby 1.9 book
about the use of === in comparisons in case statements …Is it he fact that case uses == (two ='s) for non-Class when’s and uses
=== (three ='s) for when’s that are Classes?
No (I think the following is right, but I’d welcome any corrections) -
case
always uses “===”, it’s just that the meaning of “===” may be different
to
“==”.
http://ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html
Common comparison operators, Operator Meaning
== Test for equal value.
=== Used to test equality within a when clause of a case statement.
case operates by comparing the target (the expression after the keyword
case) with each of the comparison expressions after the when keywords.
This
test is done using comparison === target. As long as a class defines
meaningful semantics for === (and all the built-in classes do), objects
of
that class can be used in case expressions.
…
Ruby classes are instances of class Class, which defines === as a test
to
see if the argument is an instance of the class or one of its
superclasses.
class Object - RDoc Documentation
obj === other => true or false
Case Equality—For class Object, effectively the same as calling #==, but
typically overridden by descendents to provide meaningful semantics in
case
statements.
class Module - RDoc Documentation
(remembering that Class is a subclass of Module, which I forget when I
initially couldn’t find the “===” method in Class)
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.
Putting that all together, and using the String class as an example:
x = “Karel Capek”
y = “Karel Capek”
p x.object_id #=> 1880688
p y.object_id #=> 1880656, so x & y are different objects
p x == y #=> true
p x === y #=> true, so for String objects “===” is the same as “==”
# at least for comparing String objects with String objects
p x == String #=> false
p x === String #=> false
p x.class == String #=> true
p String == String #=> true
p x.class === String #=> false
p String === String #=> false
p String == x #=> false
p String === x #=> true; this is what is being used in the case
statement
# case x; when String then true; else false; end
p String == x.class #=> true
p String === x.class #=> false; same as String === String
I think of === as meaning ‘matches’ rather than ‘equals’. This is
especially useful as in
case str
when /^foo/
… do something
when /^bar/
… something else
end
For classes, you have
case obj
when String
…
when Integer
…
end
Colin,
Friday, August 20, 2010, 8:05:36 PM, you wrote:
CB> On Sat, Aug 21, 2010 at 12:13 AM, Ralph S. [email protected]
wrote:
Having read your explanation and Dave T.’ Programming Ruby 1.9 book
about the use of === in comparisons in case statements …
Is it he fact that case uses == (two ='s) for non-Class when’s and uses
=== (three ='s) for when’s that are Classes?
CB> No (I think the following is right, but I’d welcome any corrections)
CB> 1. case always uses “===” for comparisons; in
CB> case x
CB> when y then
CB> the test is y === x
CB> Programming Ruby: The Pragmatic Programmer's Guide
CB> Common comparison operators, Operator Meaning
CB> == Test for equal value.
CB> === Used to test equality within a when clause of a case statement.
CB> case operates by comparing the target (the expression after the
keyword
CB> case) with each of the comparison expressions after the when
keywords. This
CB> test is done using comparison === target. As long as a class defines
CB> meaningful semantics for === (and all the built-in classes do),
objects of
CB> that class can be used in case expressions.
CB> …
CB> Ruby classes are instances of class Class, which defines === as a
test to
CB> see if the argument is an instance of the class or one of its
superclasses.
CB> 2. class Object - RDoc Documentation
obj === other =>> true or false
CB> Case Equality—For class Object, effectively the same as calling #==,
but
CB> typically overridden by descendents to provide meaningful semantics
in case
CB> statements.
CB> 3. class Module - RDoc Documentation
CB> (remembering that Class is a subclass of Module, which I forget when
I
CB> initially couldn’t find the “===” method in Class)
mod === obj =>> true or false
CB> Case Equality—Returns true if anObject is an instance of mod or one
of mod‘s
CB> descendents. Of limited use for modules, but can be used in case
statements
CB> to classify objects by class.
CB> Putting that all together, and using the String class as an example:
CB> x = “Karel Capek”
CB> y = “Karel Capek”
p x.object_id #=>> 1880688
p y.object_id #=>> 1880656, so x & y are different objects
p x == y #=>> true
p x === y #=>> true, so for String objects “===” is the same as “==”
CB> # at least for comparing String objects with String
objects
p x == String #=>> false
p x === String #=>> false
CB> p x.class == String #=> true
CB> p String == String #=> true
CB> p x.class === String #=> false
CB> p String === String #=> false
p String == x #=>> false
p String === x #=>> true; this is what is being used in the case
statement
CB> # case x; when String then true; else false; end
CB> p String == x.class #=> true
CB> p String === x.class #=> false; same as String === String
What a great answer. And, again, clear.
I can, reluctantly, see the use of === in an if statement.
To me,
case x
when XXX then …
end
would be MUCH clearer as
case x.class
when XXX.arrayOfDecendants
end
Of course, the code above is illegal … but would have been clearer …
at least to me.
Hi –
On Sat, 21 Aug 2010, Ralph S. wrote:
end
Of course, the code above is illegal … but would have been clearer … at least to me.
It doesn’t suggest the same functionality, though. This:
case obj
when XXX
runs XXX === obj (as per Colin’s explanation), and that examines whether
or not XXX is in the method look-up path of obj. This may or may not
have anything to do with obj’s class – for example:
class C; end
=> nil
module M; end
=> nil
c = C.new.extend(M)
=> #<C:0x14cd14>
case c
when M; 1
end
=> 1
If you want to know whether a given class has a certain ancestor, you
can do that too, but with a different technique:
if C.ancestors.include?(B)
etc.
In general, the === mechanism is advantageous because it means you
always know what’s going on in a case statement, and because it gives
you control over how your own objects behave in case statements.
David
–
David A. Black, Senior Developer, Cyrus Innovation Inc.
The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com
David,
Sunday, August 22, 2010, 5:15:16 PM, you wrote:
DAB> Hi –
DAB> On Sat, 21 Aug 2010, Ralph S. wrote:
I can, reluctantly, see the use of === in an if statement.
To me,
case x
when XXX then …
end
would be MUCH clearer as
case x.class
when XXX.arrayOfDecendants
end
Of course, the code above is illegal … but would have been clearer … at least to me.
DAB> It doesn’t suggest the same functionality, though. This:
DAB> case obj
DAB> when XXX
DAB> runs XXX === obj (as per Colin’s explanation), and that examines
whether
DAB> or not XXX is in the method look-up path of obj. This may or may
not
DAB> have anything to do with obj’s class – for example:
DAB> >> class C; end
DAB> => nil
DAB> >> module M; end
DAB> => nil
DAB> >> c = C.new.extend(M)
DAB> => #<C:0x14cd14>
DAB> >> case c
DAB> >> when M; 1
DAB> >> end
DAB> => 1
DAB> If you want to know whether a given class has a certain ancestor,
you
DAB> can do that too, but with a different technique:
DAB> if C.ancestors.include?(B)
DAB> etc.
DAB> In general, the === mechanism is advantageous because it means you
DAB> always know what’s going on in a case statement, and because it
gives
DAB> you control over how your own objects behave in case statements.
Responding to this last paragraph …
If I were the language designer I’d have two “cases”
(1) case== which would use the normal == semantics
(2) case=== which would use the === semantics.
Again … at least this would be clearer to ME.
DAB> David
Maybe when I understand Ruby a lot better than I do that the explanation
you provided will make more sense.
Hi –
On Mon, 23 Aug 2010, Ralph S. wrote:
DAB> => nil
explanation you provided will make more sense.
Here’s some annotation:
Every object has a lookup path, consisting of classes and modules in a
particular order, which it traverses when it’s trying to resolve a
method name. If you extend an object with a module (see above), you
insert that module into the lookup path of the object. Any module or
class that’s in the lookup path of an object (including, but not limited
to, the object’s class and modules included in that class) will match
the object for purposes of case equality.
So what I was getting at in my example is that knowing an object’s
class, and even that class’s ancestors, doesn’t tell you everything that
Module#=== tells you. In the example, the “1” shows that M === c, even
though c’s class does not include M.
David
–
David A. Black, Senior Developer, Cyrus Innovation Inc.
The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.
Sponsor our Newsletter | Privacy Policy | Terms of Service | Remote Ruby Jobs