5

This is mostly an “academic” one but here it goes:

According to this Ruby eigenclass diagram (slightly edited):

Possibly-wrong Ruby eigenclass diagram

BasicObject.singleton_class.singleton_class.superclass is Class.

However, running this on a Ruby interpreter (Ruby v2.5.1), it turns out that BasicObject.singleton_class.singleton_class.superclass is #<Class:Class> and not Class. Therefore, is the diagram lying or am I missing something?

The diagram is from a user I chatted with at Ruby IRC in Freenode. However, it's been quoted multiple times to many other users and it's been treated as the Ruby object model bible.

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
stratis
  • 7,750
  • 13
  • 53
  • 94
  • 1
    `BasicObject.singleton_class.singleton_class.is_a?(Class) #⇒ true` `#Class` is a string representation. – Aleksei Matiushkin Jul 02 '18 at 08:59
  • 2
    Your question says `BasicObject.singleton_class.singleton_class` but your diagram says `BasicObject.singleton_class.singleton_class.superclass`. Please clarify. – Stefan Jul 02 '18 at 09:02
  • @Stefan @mudasobwa You are right. I meant to say `BasicObject.singleton_class.singleton_class.superclass`. I also edited the question. Any clues are more than welcome. – stratis Jul 02 '18 at 09:18
  • @kstratis: not sure what confusion is still there after mudasobwa's clarification. It IS a Class. – Sergio Tulentsev Jul 02 '18 at 09:37
  • @SergioTulentsev According to the diagram `BasicObject.singleton_class.singleton_class.superclass` is simply `Class`. However in reality is the singleton class of `Class` which is `#Class`. It can't be both. Which one is correct and why? – stratis Jul 02 '18 at 09:39
  • 1
    @kstratis: ah, I see. Well, when a diagram conflicts with reality, the answer to "which is correct" is obvious. :) – Sergio Tulentsev Jul 02 '18 at 09:41
  • 1
    You keep saying `#Class`. What's your ruby version and/or env where you see this? for me it's `#` – Sergio Tulentsev Jul 02 '18 at 09:41
  • @SergioTulentsev Right. `#` is what I meant. `#Class` was just a shortcut I made up for brevity. Using Ruby v2.5.1 on macOS. – stratis Jul 02 '18 at 09:44
  • 1
    Indeed, this is confusing. Where is that diagram from? – Sergio Tulentsev Jul 02 '18 at 09:44
  • Yeah, don't use confusing shortcuts that only you are aware of. We programmers like exactness :) – Sergio Tulentsev Jul 02 '18 at 09:45
  • It's from a user I chatted with at Ruby IRC in freenode. However it's been quoted multiple times to many other users and it's been treated as the Ruby object model bible... Turns out it's got its flaws... – stratis Jul 02 '18 at 09:45
  • I'm betting it _was_ accurate at the time. If only we could find out what ruby version was current at the moment of making the diagram. – Sergio Tulentsev Jul 02 '18 at 09:47
  • Or who made this (so that we can tweet at them, or something) – Sergio Tulentsev Jul 02 '18 at 09:48
  • 1
    I like your train of thought. This COULD be true. Maybe it was accurate at the time. On the other hand I haven't heard of any object model changes in Ruby in recent years. Anyway to tell you the truth I don't remember. It was months ago. I'll try to ping a moderator at the IRC and will update the post in case of any updates. Thanks for the help anyway. – stratis Jul 02 '18 at 09:53
  • 2
    Checked a few ruby versions down to 1.9.3. All exhibit the same behaviour. Guess we were reading the wrong bible all this time :) – Sergio Tulentsev Jul 02 '18 at 10:01

1 Answers1

2

The behavior of the Ruby interpreter makes perfect sense because:

  • When a Child class extends a Parent, Ruby sets it up so that the singleton class #<Class:Child> extends #<Class:Parent> as well; and
  • BasicObject.singleton_class is a subclass of Class, so BasicObject.singleton_class.singleton_class will be a subclass of #<Class:Class>

Verifying the equality:

BasicObject.singleton_class.singleton_class.superclass.equal?(Class.singleton_class)
#=> true

This leads to the next question – why does #<Class:BaseObject> extend Class in the first place? Following the rule above, since BaseObject has no superclass – that is, BaseObject.superclass is nil – the logical thing would be for its singleton class to not have a superclass either.

The answer is that #<Class:BaseObject> extending Class ensures consistency in the inheritance hierarchy when it comes to singleton classes. Take this Ruby object for example:

obj = "a string"

It is a well-established notion that instead of obj being simply an instance of String, we can think of it as an (only) instance of its own singleton class, which in turn is a subclass of String. That is:

obj.class.equal?(obj.singleton_class.superclass)
#=> true

It seems only logical that the same should apply to class instances as well. But it does not, because it contradicts the rule mentioned above, where the superclass of a singleton class of a Child class is the singleton class of its Parent class.

class Foo; end

Foo.class
#=> Class

Foo.singleton_class.superclass
#=> #<Class:Object>      <-- not equal to Class!

# because:
Foo.superclass
#=> Object

But it is possible to resolve this contradiction by placing Class at the top of the singleton class inheritance hierarchy:

Foo.singleton_class.superclass
#=> #<Class:Object>

Foo.singleton_class.superclass.superclass
#=> #<Class:BasicObject>

Foo.singleton_class.superclass.superclass.superclass
#=> Class

This way, even though Foo.singleton_class.superclass is not equal to Foo.class, by walking up the inheritance chain, it does get there eventually...

Mate Solymosi
  • 5,699
  • 23
  • 30