Forum: Ruby Module#=== vs Object#is_a?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Peter F. (Guest)
on 2009-02-07 14:16
(Received via mailing list)
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 F.
(847) 687-7646
Email: removed_email_address@domain.invalid
IM GTalk: peter.fitzgibbons
IM Yahoo: pjfitzgibbons
IM MSN: removed_email_address@domain.invalid
IM AOL: removed_email_address@domain.invalid
Stefano C. (Guest)
on 2009-02-07 14:28
(Received via mailing list)
Alle Saturday 07 February 2009, Peter F. ha scritto:
>
Looking at the ri documentation for the two methods, I'd say they do
exactly
the same thing.

Stefano
Tom L. (Guest)
on 2009-02-07 14:47
(Received via mailing list)
> Could somone explain the subtle difference between Module#=== and
> Object#is_a?

=== is used in case statements and can be overridden in subclasses.
David A. Black (Guest)
on 2009-02-07 14:50
(Received via mailing list)
On Sat, 7 Feb 2009, Peter F. wrote:

> 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!
Igor P. (Guest)
on 2009-02-08 00:00
Peter F. wrote:
> 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 P.ming 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)
Peter F. (Guest)
on 2009-02-08 00:52
(Received via mailing list)
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 F.
(847) 687-7646
Email: removed_email_address@domain.invalid
IM GTalk: peter.fitzgibbons
IM Yahoo: pjfitzgibbons
IM MSN: removed_email_address@domain.invalid
IM AOL: removed_email_address@domain.invalid
David A. Black (Guest)
on 2009-02-08 01:28
(Received via mailing list)
Hi --

On Sun, 8 Feb 2009, Peter F. wrote:

> 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!
Robert D. (Guest)
on 2009-02-08 15:23
(Received via mailing list)
On Sun, Feb 8, 2009 at 12:27 AM, David A. Black 
<removed_email_address@domain.invalid>
wrote:
>
>  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
.

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.
Cheers
Robert
David A. Black (Guest)
on 2009-02-08 15:46
(Received via mailing list)
Hi --

On Sun, 8 Feb 2009, Robert D. wrote:

>
> 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!
Robert D. (Guest)
on 2009-02-08 19:39
(Received via mailing list)
On Sun, Feb 8, 2009 at 2:44 PM, David A. Black 
<removed_email_address@domain.invalid>
wrote:

>
> 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
David A. Black (Guest)
on 2009-02-08 20:02
(Received via mailing list)
Hi --

On Mon, 9 Feb 2009, Robert D. wrote:

> using #=== on well defined classes, Class and Regexp that is, does not
> 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!
Robert D. (Guest)
on 2009-02-08 20:35
(Received via mailing list)
On Sun, Feb 8, 2009 at 7:01 PM, David A. Black 
<removed_email_address@domain.invalid>
wrote:

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.
Brian C. (Guest)
on 2009-02-09 15:25
Peter F. wrote:
> 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.
Clifford H. (Guest)
on 2009-02-10 01:51
(Received via mailing list)
Robert D. wrote:
> 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 H..
This topic is locked and can not be replied to.