Class === class often false?

Hi,

I’m just noticing that some classes return true when testing
themselves with #===, while others return false. For instance:

(ruby 1.6.6 (2001-12-26) [i586-mswin32])

Bignum === Bignum
false
Bignum.id === Bignum.id
true
Bignum == Bignum
true
Bignum.ancestors
[Bignum, Integer, Precision, Numeric, Comparable, Object, Kernel]
Kernel === Kernel
true
Object === Object
true
Comparable === Comparable
false

. . . It appears Comparable may be the culprit? Though, I’ve
noticed Enumerable introduces the same kind of behavior.

Just wondering if this behavior is intentional, and if so, why?

Thanks,

Bill

I’m just noticing that some classes return true when testing
themselves with #===, while others return false. For instance:

(ruby 1.6.6 (2001-12-26) [i586-mswin32])

Bignum === Bignum
false
Kernel === Kernel
true
Object === Object
true
Comparable === Comparable
false

(some snips)

Just wondering if this behavior is intentional, and if so, why?

I’ll take a stab at this one.

If I’m wrong at any point, someone please correct me.

The === operator (method) may mean different things
depending on what class it belongs to. That’s no
big news.

When the receiver and the other operand (x === y)
are both classes, it is (I think) essentially the
same as saying:

x.is_a? y

So far, so good.

Note also that if a class includes a module, it
then has the “is a” relationship with that module,
and an included module shows up in the list of
ancestors. (Since a module is a kind of parent –
it’s like a limited form of inheritance.)

Then note that there is some circularity in Ruby’s
object paradigm. Everything is an object. Even the
class Class is an object. Note that these are
both true:

Class.is_a? Object
Object.is_a? Class

I have spent many hours lying in bed thinking about
this. :slight_smile:

I think the cases where you’re getting a “true” for
"klass === klass" are the ones where the circularity
is asserting itself.

For example: Kernel is-a Object, but Object is-a Kernel.
Or more indirectly: Kernel is-a Module is-a Object is-a
Kernel. There are probably other paths (through Class,
which is-a Module).

For the cases where you get false, I can’t see any
way of tracing back to oneself. For example, Bignum
is-a Object, or is-a Class, or is-a Kernel… but these
don’t ever lead back to Bignum.

Help any?

Hal

···

----- Original Message -----
From: “Bill Kelly” billk@cts.com
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Monday, August 12, 2002 6:52 PM
Subject: class === class often false?

ri Module.=== gives <<EOD

------------------------------------------------------------- Module#===
mod === anObject -> 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.

EOD

The main thing here is that when you type Bignum === x, or Comparable ===
x, Ruby expects to be an Object. Now of course, everything is an Object,
but it is their TYPE that is being inspected.

So:

  • Bignum === Bignum: the RHS is an object of type Class, which does not
    feature in Bignum’s ancestors; hence false.

  • Kernel === Kernel: everything has Kernel included in it, so x ===
    Kernel is always true.

  • Object === Object: ditto (every Class is_a? Object)

  • Comparable === Comparable: same as Bignum

  • Bignum.id === Bignum.id: === reduces to == in this case (Fixnum), hence
    true

In short, you’re not using the === operator in the documented way.

Cheers,
Gavin

Hi,

I’m just noticing that some classes return true when testing
themselves with #===, while others return false. For instance:

(ruby 1.6.6 (2001-12-26) [i586-mswin32])

Bignum === Bignum
false
Bignum.id === Bignum.id
true
Bignum == Bignum
true
Bignum.ancestors
[Bignum, Integer, Precision, Numeric, Comparable, Object, Kernel]
Kernel === Kernel
true
Object === Object
true
Comparable === Comparable
false

. . . It appears Comparable may be the culprit? Though, I’ve
noticed Enumerable introduces the same kind of behavior.

Just wondering if this behavior is intentional, and if so, why?

Thanks,

Bill

Hi –

I’ll take a stab at this one.

If I’m wrong at any point, someone please correct me.

Likewise, and likewise :slight_smile:

The === operator (method) may mean different things
depending on what class it belongs to. That’s no
big news.

When the receiver and the other operand (x === y)
are both classes, it is (I think) essentially the
same as saying:

x.is_a? y

It’s actually y.is_a? x

irb(main):003:0> String === Object
false
irb(main):004:0> Object === String
true

which I think is to help with case statement syntax:

case String
when Object…
etc.

So far, so good.

Note also that if a class includes a module, it
then has the “is a” relationship with that module,
and an included module shows up in the list of
ancestors. (Since a module is a kind of parent –
it’s like a limited form of inheritance.)

It is among the ancestors, but there’s no is_a? or === relationship:

irb(main):019:0> Array.ancestors
[Array, Enumerable, Object, Kernel]
irb(main):020:0> Array.is_a? Enumerable
false
irb(main):021:0> Enumerable === Array
false

However, Array objects do return true on these tests:

irb(main):026:0> [].is_a? Enumerable
true
irb(main):027:0> Enumerable === []
true

A (whimsical) further test:

irb(main):036:0> class Class; include Enumerable; end
Class
irb(main):037:0> Enumerable === Array
true

So ancestors/is_a?/Module#=== are related but can vary independently.

Now, this one:

irb(main):015:0> Kernel === Object
true

is a little puzzling to me, because I would have thought Object:Kernel
was the same relationship as Array:Enumerable. Except… there is
that circularity/singularity at the top of the chain – which I
alternate between thinking I understand and losing sleep over :slight_smile: In
this case I can’t say I entirely understand exactly how/why it would
account for the result, but I think somehow it does.

David

···

On Tue, 13 Aug 2002, Hal E. Fulton wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Hi Hal,

From: “Bill Kelly” billk@cts.com

I’m just noticing that some classes return true when testing
themselves with #===, while others return false. For instance:

(ruby 1.6.6 (2001-12-26) [i586-mswin32])

Bignum === Bignum
false
Kernel === Kernel
true
Object === Object
true
Comparable === Comparable
false

[…]

Then note that there is some circularity in Ruby’s
object paradigm. Everything is an object. Even the
class Class is an object. Note that these are
both true:

Class.is_a? Object
Object.is_a? Class

I have spent many hours lying in bed thinking about
this. :slight_smile:

:slight_smile:

I think the cases where you’re getting a “true” for
"klass === klass" are the ones where the circularity
is asserting itself.

For example: Kernel is-a Object, but Object is-a Kernel.
Or more indirectly: Kernel is-a Module is-a Object is-a
Kernel. There are probably other paths (through Class,
which is-a Module).

For the cases where you get false, I can’t see any
way of tracing back to oneself. For example, Bignum
is-a Object, or is-a Class, or is-a Kernel… but these
don’t ever lead back to Bignum.

Help any?

Interesting… further tests in irb seem to support your
hypothesis.

Thanks! . . . Actually what I finally realized was that
Ruby was already doing what I wanted in case statements.
I’m dealing with an instance, so === doing is_a? is just
what I want after all. I had been trying:

case some_instance.class
when Bignum
etc.

Where I can simply do (of course)

case some_instance
when Bignum
etc.

Which is great (as usual.)

Regards,

Bill

···

From: “Hal E. Fulton” hal9000@hypermetrics.com

Hi –

The main thing here is that when you type Bignum === x, or Comparable ===
x, Ruby expects to be an Object. Now of course, everything is an Object,
but it is their TYPE that is being inspected.

So:

  • Bignum === Bignum: the RHS is an object of type Class, which does not
    feature in Bignum’s ancestors; hence false.

I don’t think that logic works – it implies that if the RHS were of a
type which did feature in Bignum’s ancestors, the test would be true.
But Bignum’s ancestors include Object, and the RHS can’t be an
arbitrary object :slight_smile:

I think it’s more that the LHS has to be among the ancestors of the
type of the RHS. Bignum is not an ancestor of Class, so the result is
false.

  • Kernel === Kernel: everything has Kernel included in it, so x ===
    Kernel is always true.

Other way around :slight_smile: Kernel === x is always true.

David

···

On Tue, 13 Aug 2002, Gavin Sinclair wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

It’s actually y.is_a? x

Oh, yes.

which I think is to help with case statement syntax:

Yes.

Note also that if a class includes a module, it
then has the “is a” relationship with that module,
and an included module shows up in the list of
ancestors. (Since a module is a kind of parent –
it’s like a limited form of inheritance.)

It is among the ancestors, but there’s no is_a? or === relationship:

(snip where you prove this)

Hmm, brain fade on my part. Happens daily.

However, Array objects do return true on these tests:

irb(main):026:0> [].is_a? Enumerable
true
irb(main):027:0> Enumerable === []
true

That must be what I was thinking.

A (whimsical) further test:

irb(main):036:0> class Class; include Enumerable; end
Class
irb(main):037:0> Enumerable === Array
true

So ancestors/is_a?/Module#=== are related but can vary independently.

Yes, this is where my brain itches again.

Now, this one:

irb(main):015:0> Kernel === Object
true

is a little puzzling to me, because I would have thought Object:Kernel
was the same relationship as Array:Enumerable. Except… there is
that circularity/singularity at the top of the chain – which I
alternate between thinking I understand and losing sleep over :slight_smile: In
this case I can’t say I entirely understand exactly how/why it would
account for the result, but I think somehow it does.

Probably true.

I’ll think about this more later.

Hal

···

----- Original Message -----
From: dblack@candle.superlink.net
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Monday, August 12, 2002 8:17 PM
Subject: Re: class === class often false?

Hi –

A (whimsical) further test:

irb(main):036:0> class Class; include Enumerable; end
Class
irb(main):037:0> Enumerable === Array
true

So ancestors/is_a?/Module#=== are related but can vary independently.

Yes, this is where my brain itches again.

I feel some culpability, given what a cop-out that concluding sentence
is :slight_smile: But we’re in this together… I definitely want to clear my
own mental cobwebs in this area.

OK, after some further tinkering, here’s what I think:

  1. “Mod === obj” and “obj.is_a? Mod” are in effect the same test.

  2. Module#ancestors can be confusing because it’s not
    Object#ancestors. I mean, it’s right that it isn’t, but one finds
    oneself expecting “abc”.ancestors to be String, Enumerable… (At
    least, one does if one is me, or one is I, or one == I, or I === one.)
    But only modules and classes have ancestors, and all ancestors are
    modules or classes. This is a case where, even though they are
    regular objects, modules and classes get to be different. (I repeat:
    it makes complete sense that there’s no Object#ancestors. The problem
    is getting myself to grasp it.)

  3. However, every object does respond to #type. In every case –
    whether the RHS is a module or not – “Mod === obj” tests whether
    obj’s type is among Mod’s ancestors.

  4. All of this means that this:

    Enumerable === String

which perhaps looks like it’s asking whether strings are Enumerable,
is in fact testing whether the object String is of a type which counts
Enumerable among its ancestors. Class (String’s type) does not do
that, so this test is false.

Jotting down a possibly helpful train of thought:

Are Array objects Enumerable? Let’s test one:

Enumerable === [] # true

Are Class objects Enumerable? Let’s test one:

Enumerable === String # false

I’ll leave the Kernel/Object/Module/Class circularity for another time
:slight_smile:

David

···

On Tue, 13 Aug 2002, Hal E. Fulton wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Whoops, I knocked the ? off the subject line on my last post. Well,
it’s probably reasonably clear that it’s supposed to be part of this
thread :slight_smile:

David

···


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav