Singleton class of singleton class of BasicObject in Ruby

400 Views Asked by At

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.

1

There are 1 best solutions below

0
Mate Solymosi On

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...