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