Module#=== vs Object#is_a?

  • Thread starter Peter Fitzgibbons
  • Start date
P

Peter Fitzgibbons

[Note: parts of this message were removed to make it a legal post.]

HI All,

Could somone explain the subtle difference between Module#=== and
Object#is_a?

I found a usage of === to test if an argument is Regexp or not... just want
to know the restrictions.
The code is beautiful...

if Regexp === regexp_or_string


Peter Fitzgibbons
(847) 687-7646
Email: (e-mail address removed)
IM GTalk: peter.fitzgibbons
IM Yahoo: pjfitzgibbons
IM MSN: (e-mail address removed)
IM AOL: (e-mail address removed)
 
S

Stefano Crocco

Alle Saturday 07 February 2009, Peter Fitzgibbons ha scritto:
HI All,

Could somone explain the subtle difference between Module#=== and
Object#is_a?

I found a usage of === to test if an argument is Regexp or not... just want
to know the restrictions.
The code is beautiful...

if Regexp === regexp_or_string

Looking at the ri documentation for the two methods, I'd say they do exactly
the same thing.

Stefano
 
D

David A. Black

HI All,

Could somone explain the subtle difference between Module#=== and
Object#is_a?

I found a usage of === to test if an argument is Regexp or not... just want
to know the restrictions.
The code is beautiful...

if Regexp === regexp_or_string

They're the same, in essence. === is bound to the C method rb_mod_eqq,
which looks like this:

static VALUE
rb_mod_eqq(VALUE mod, VALUE arg)
{
return rb_obj_is_kind_of(arg, mod);
}

=== is usually a light wrapper around something, mainly so that the
case construct can use it implicitly. (It always looks a bit odd to me
used explicitly, since it usually has semantics that don't relate to
anything that equal signs normally signify.)


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
 
I

Igor Pirnovar

Peter said:
HI All,

Could somone explain the subtle difference between Module#=== and
Object#is_a?

I found a usage of === to test if an argument is Regexp or not... just
want
to know the restrictions.
The code is beautiful...

if Regexp === regexp_or_string

The Class class defines === operator in such a way that it can be used
in place of is_a.
+---------------------------------------------+
| x = 1 |
| Numeric === x # => true: x is_a Numeric |
+---------------------------------------------+
This is exactly the same use as your:
+---------------------------------------------+
| if Regexp === regexp_or_string |
+---------------------------------------------+

All the technical details about Ruby implementation of === and is_a are
pretty much irrelevant. What is important is the meaning of the test we
perform with these and similar operators. As others already pointed out
"===" and "is_a" are used to obtain exactly the same info about
instances or classes. You can find the best explanation about this in
the defacto Ruby bible called "The Ruby Programming Language" by D.
Flanagan and Y. Matsumoto, in chapter "8.1 Types, Clases and Modules".
Let me copy it out here:

The most commonly used reflective methods are those for determining the
type of an object - what class it is an instance of, and what methods it
responds to. ... To review:

o.class
Returns the class of an object o.
c.superclass
Returns the superclass of a class c.
o.instance_of? c
Determines whether the object o.class == c.
o.is_a? c
Determines whether o is an instance of c, or of any of its
subclasses. If c is a module, this method tests wether o.class (or any
of its ancestors) includes the module.
o.kind_of? c
kind_of? is a synonym for is_a?
c === o
For any class or module c, determines if o.is_a?(c)
o.responds_to? name
Determines whether the object o has a public or protected method
with the specified name. Pass true as the second argument to check
private methods as well. i.e: o.responds_to?("name", true)
 
P

Peter Fitzgibbons

[Note: parts of this message were removed to make it a legal post.]

I love it!

Thank you all for the discourse.

I take Igor's reference to the Ruby bible as the most authoritative
explanation, which directly indicates that these two are *intended* to be
equivalent constructs.

What I like about === most is that it was very elegant looking AND made me
stop to go find out exactly what was going on. Metaprogramming beauty. I
suppose that goes against the grain of some who wish for the code to tell
you clearly what is going on without reference... but how would you know
what <=> or ||= are without reference?

So, I'll be using === instead of is_a?

Thanks all!

Peter Fitzgibbons
(847) 687-7646
Email: (e-mail address removed)
IM GTalk: peter.fitzgibbons
IM Yahoo: pjfitzgibbons
IM MSN: (e-mail address removed)
IM AOL: (e-mail address removed)
 
D

David A. Black

Hi --

I love it!

Thank you all for the discourse.

I take Igor's reference to the Ruby bible as the most authoritative
explanation, which directly indicates that these two are *intended* to be
equivalent constructs.

Yes, it wasn't an accident :) It's in keeping with the purpose of
===. Basically, === exists so that it can be overridden in classes
(including, but not limited to, Module), and will then exhibit
appropriate behavior for different classes, in case statements.

Thus, for example, Regexp#=== does a match test:

case "abc"
when /abc/ ... # /abc/ === "abc"
end
What I like about === most is that it was very elegant looking AND made me
stop to go find out exactly what was going on. Metaprogramming beauty. I
suppose that goes against the grain of some who wish for the code to tell
you clearly what is going on without reference... but how would you know
what <=> or ||= are without reference?

It's great to learn what it all means, but I wouldn't necessarily use
some of these intentionally generic methods explicitly. For example, I
would rather do this:

a > b

than this:

(a <=> b) == 1

though of course I want to know what <=> and how to implement my own
<=> methods when necessary. The same holds (even more so in a way,
since <=> at least has consistent semantics) for ===, which is
designed to be overridden and therefore fluid as to its semantics and
meaning.

=== is very cool because it lets you define your own case statements
and have them work based on a method, without your having to call the
method explicitly. And of course if you make up your own meanings for
===, they're not going to be as obvious as the ones for the built-in
classes (which are few enough that it's not hard to learn them, even
though they usually have more expressive alternatives). For example,
it's better to write:

if car.make == make

than

if make === car

which is not indecipherable but which definitely does need to be
deciphered and which even then has a peculiar backwards ring to
it. You might, however, want make objects to be engineered so that
they can case-compare themselves with a car:

case car
when Ford ...

etc., but the explicit === can be a bit too much of a "secret
handshake".


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
 
R

Robert Dober

if car.make == make

than

if make === car
Although I am a big fan with using #=== (for fingerlazyness) I have to
agree, after some months of thinking and reading it is probably a bad
idea to use it too much.

I am still not sure for some special cases as e.g.
instance_of_Class === object
or
instance_of_Regex === string
these idioms have some good points too e.g. auto-documenting case
behavior and forcing nubies to learn about #===.

However I really do have a strong opinion ( very loosely hold of course! ) about
make === car
and
car.make == make
 
D

David A. Black

Hi --

Although I am a big fan with using #=== (for fingerlazyness) I have to
agree, after some months of thinking and reading it is probably a bad
idea to use it too much.

I am still not sure for some special cases as e.g.
instance_of_Class === object
or
instance_of_Regex === string
these idioms have some good points too e.g. auto-documenting case
behavior and forcing nubies to learn about #===.

I don't think it's the right approach to learning, though. I'd rather
see the best possible code, and let people learn from that. No one is
going to really learn about #=== (the fact that it's the case equality
operator, etc.) just by seeing examples of its explicit use.
However I really do have a strong opinion ( very loosely hold of course! ) about
make === car
and
car.make == make
.

I would think that if one accepted the first idiom in a project it
really should convey a very, very strong message, something so
intrinsic to the problem space that a special idiom is justified.
In the general case I agree with David though.

I'm not sure you need a "though"; that's essentially the point I was
making :)


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
 
R

Robert Dober

I'm not sure you need a "though"; that's essentially the point I was
making :)
Actually the 'though' was here because we do not agree on the special
usecase for #=== I am quite fond of, and I know that. But that was
quite cryptic for the rest of the world, sorry.

BTW it would help me a lot to understand better why my approach of
using #=== on well defined classes, Class and Regexp that is, does not
help to learn.
This is not about bad code or good code, because I will just not agree that
/===/ === "==="
is bad code ;). [ In my eyes this is probably the most beautiful LOC I
have *ever* written, LOL]

My question would go to the teacher as follows:
Would students not be curious about seeing an unfamiliar idiom? Is
that not often the motivation of posting questions on this list and
sometimes having quite interesting threads.
Funily I try to prove my assumption with this thread, sort of :).
If you feel this is too OT, never mind.

Thanx
Robert
 
D

David A. Black

Hi --

I'm not sure you need a "though"; that's essentially the point I was
making :)
Actually the 'though' was here because we do not agree on the special
usecase for #=== I am quite fond of, and I know that. But that was
quite cryptic for the rest of the world, sorry.

BTW it would help me a lot to understand better why my approach of
using #=== on well defined classes, Class and Regexp that is, does not
help to learn.
This is not about bad code or good code, because I will just not agree that
/===/ === "==="
is bad code ;). [ In my eyes this is probably the most beautiful LOC I
have *ever* written, LOL]

My question would go to the teacher as follows:
Would students not be curious about seeing an unfamiliar idiom? Is
that not often the motivation of posting questions on this list and
sometimes having quite interesting threads.
Funily I try to prove my assumption with this thread, sort of :).
If you feel this is too OT, never mind.

I'm not making a pronouncement that no one should ever see an
unfamiliar idiom in Ruby. That wouldn't make sense, since we're not
born knowing Ruby :)

But I don't think that Ruby practitioners should decide which idioms
to use based on choosing the one that newcomers are least likely to be
able to understand. The process of people seeing code they don't
understand and asking questions about it is not endangered. It will
keep happening, and it's not really micro-manageable anyway; I don't
think one can plant a particular idiom somewhere with any knowledge
that a nuby is going to spot exactly that usage and wonder about it.


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
 
R

Robert Dober

Thank you David
Am I correct that you mean that if I believe that writing code like
/===/ === "===" # I am really in love with this one ;)
this because I, mistaken or not, think it is good code than of course
this is ok.
However if my motivation would be to introduce strange idioms in order
to "challenge" everbody to become a "better" programmer this is
probably an ineffective reasoning.
Well makes lots of sense to me maybe I was trying to be a missionary,
not something I want to be at all, but we are not always aware of our
dark sides. Enough I am completely OT now....
R.
 
B

Brian Candler

Peter said:
HI All,

Could somone explain the subtle difference between Module#=== and
Object#is_a?

I found a usage of === to test if an argument is Regexp or not... just
want
to know the restrictions.
The code is beautiful...

if Regexp === regexp_or_string

case regexp_or_string
when Regexp
.. do something
when String
.. do something else
else
raise "Not a regexp or string"
end

This is the same thing though: case x when y .. does a "y === x" test
under the hood.
 
C

Clifford Heath

Robert said:
Although I am a big fan with using #=== (for fingerlazyness) I have to
agree, after some months of thinking and reading it is probably a bad
idea to use it too much.

Sorry I came late to this party. I agree, and I can tell you why.
I'm using SimpleDelegate and mixins quite a bit, and I also have an
implementation of multiple inheritance (in my ActiveFacts project).

SimpleDelegate delegates the #class method, so d.class returns the
class of the thing it's delegating for... but === is defined in the
C code to use the internal class pointer; so === doesn't honour
the delegation:

class Bar; end
class Foo
def class; Bar; end
end
f=Foo.new
f.class => Bar
Bar === f => false

Furthermore, if I say "MyClass === thing", I'm asking MyClass to
determine whether "thing" is "people like us". There's no way you
can do anything in "thing" to spoof that - which is fundamentally
at odds with the principle of duck typing. If you want to quack like
a duck, I *shouldn't be able to tell* that you aren't.

On the other hand, "thing.is_a? MyClass" is a method on "thing", so
it gets to decide if it's the kind of thing you care about. This even
works when you define is_a? to give an affirmative response to multiple
unrelated classes; i.e. multiple inheritance:

class Bar; end
class Foo
def is_a? klass
super || klass == Bar
end
end
f=Foo.new => #<Foo:0x74b6c>
f.is_a?(Foo) => true
f.is_a?(Bar) => true

This required me to convert almost all occurrences of === from my
code. If you want to say you're an Array, I'll believe you and talk
to you like an Array - it's not up to me to poke inside your C
definition and undermine your modified #class method, as === does.

I suspect this behaviour varies between Ruby variants too.

Clifford Heath.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top