This is mostly an “academic” one but here it goes:
According to this Ruby eigenclass diagram (slightly edited):
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.
The behavior of the Ruby interpreter makes perfect sense because:
Child
class extends a Parent
, Ruby sets it up so that the singleton class #<Class:Child>
extends #<Class:Parent>
as well; andBasicObject.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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With