The focus of this article will be on one of the more ethereal and little-discussed properties of Singleton Classes namely an answer to the question: “What is the class of a singleton class?”. Exactly this question was asked in chapter 4 of the Ruby Hacking Guide; and the answer was purportedly depicted by the following diagram:

Beneath the diagram was written this text:

“A class’s singleton class puts itself as its own class. Quite complicated.”.

And that was where the matter was left to stand with no further discussion or analysis.

What follows, then, is my own research into the matter.

(applies only to Ruby 1.8.6 and 1.8.7).

What is the class of a Singleton Class?

First we can try a naive approach:

class Fren; end class_of_meta = class << Fren; self; end.class puts class_of_meta #output is Class

The above result does not match what was stated in the RHG, indeed if the RHG is correct the output should be the Singleton Class of Fren.

However, we know we cannot necessarily trust the result from Object#class. Ruby tries to hide the existence of Singleton Classes from us and the Object#class method will never return a Singleton Class. Here is the source code for the Object#class method:

VALUE rb_obj_class(obj) VALUE obj; { return rb_class_real(CLASS_OF(obj)); }

and for the rb_class_real() function:

VALUE rb_class_real(cl) VALUE cl; { while (FL_TEST(cl, FL_SINGLETON) || TYPE(cl) == T_ICLASS) { cl = RCLASS(cl)->super; } return cl; }

The CLASS_OF macro used in rb_obj_class() represents the _actual_ class of an object, and will quite happily expand to Singleton Classes or IClasses (you can learn more about IClasses here).

The rb_class_real() function on the other hand is responsible for taking a class and locating a superclass of it that is not a Singleton Class or an IClass.

The overall effect of rb_obj_class() therefore is that it may prevaricate; that is it may not return the _actual_ class of your object but instead one of its superclasses.

In order to find the actual class of a class, therefore, we must write our own function:

VALUE actual_class(VALUE obj) { return CLASS_OF(obj); }

Now, let’s try to find the class of Fren’s Singleton Class using our new Object#actual_class method:

class Fren; end class_of_meta = class << Fren; self; end.actual_class puts class_of_meta #output is #<Class:Class>

The result we get is the Singleton Class of Class, we also know that this result is accurate because we wrote our own function.

So, according to our tests the class of this Singleton Class is not itself (as stated in RHG) but in fact the Singleton Class of Class. Does this mean the RHG is wrong, or what is going on?

To answer this we must look at the precise place in the Ruby sourcecode where Singleton Classes are created: the function rb_make_metaclass(): (note that the terminology in the sourcecode gets confusing, please go here to find a definition of the terms, however I will continue to just use ‘Singleton Class’ for the duration of this article)

VALUE rb_make_metaclass(obj, super) VALUE obj, super; { VALUE klass = rb_class_boot(super); FL_SET(klass, FL_SINGLETON); RBASIC(obj)->klass = klass; rb_singleton_class_attached(klass, obj); if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) { RBASIC(klass)->klass = klass; RCLASS(klass)->super = RBASIC(rb_class_real(RCLASS(obj)->super))->klass; } else { VALUE metasuper = RBASIC(rb_class_real(super))->klass; /* metaclass of a superclass may be NULL at boot time */ if (metasuper) { RBASIC(klass)->klass = metasuper; } } return klass; }

The important thing to note in the above code is that there are TWO cases, each of which rb_make_metaclass() treats differently.

The first case is defined by the condition in the if, notably:

if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON))

Which means in English “if the class you want to make a Singleton Class for is itself a Singleton…”

This leaves the second case to mean: “if the class you want to make a Singleton Class for IS NOT itself a Singleton…”

Let us consider the second case first where we are building Singleton Classes for classes that are not themselves Singletons. This is the most common case we encounter in Ruby land. Whenever we do something like this:

o = Object.new class << o; end

or even something like this: (the Metaclass for a class is automatically created on class creation)

class Fren; end

we are are creating a Singleton Class for a class that is not itself a Singleton. For this case rb_make_metaclass() will do the following (note I have removed the if(metasuper) for clarity):

VALUE metasuper = RBASIC(rb_class_real(super))->klass; RBASIC(klass)->klass = metasuper;

What the above code does is sets the class of the Singleton Class (‘klass’ in the source) to the class of its first “real” (read: non-Singleton or IClass) superclass.

Let’s see what this means in practice; but first let us define some notation to aid us. From here on SING(obj) will refer to the Singleton Class of obj, SUPER(obj) will refer to the superclass and CLASS(obj) will indicate the class of obj. Now examine the following code:

class Fren; end john = Fren.new class_of_meta = class << john; self; end.actual_class

In the above we know that SING(john) will be inserted into john’s inheritance hierarchy right below Fren; that is the class of john will be set to SING(john) with Fren as its superclass (go here to find out why).

Now let’s apply the rule from rb_make_metaclass(): “the class of john’s Singleton Class is equal to the class of its first real superclass.”

As stated earlier we know that the superclass of SING(john) is Fren. We also know that Fren is a ‘real class’ (not a Singleton or an IClass) therefore the class of SING(john) should be equal to the class of Fren. Now since Fren is a class it comes into the world already with its own Singleton Class (SING(Fren)) and the class of Fren is set to SING(Fren).

Therefore, according to the rule from rb_make_metaclass() the class of SING(john) should be SING(Fren), or in notation: CLASS(SING(john)) = SING(Fren). Let’s test this:

puts class_of_meta #output is #<Class:Fren>

Bingo! Now let’s try for the case of classes, repeating the code we tried at the start of this article and see if we can make sense of the result we attained:

class Fren; end class_of_meta = class << Fren; self; end.actual_class

Let’s now apply the rule “the class of Fren’s Singleton Class is equal to the class of its first real superclass”. We know that the immediate superclass of SING(FREN) is actually SING(Object) (to find out why go here) and we also know that the superclass of SING(Object) is Class.

‘Class’ itself, then, is the first ‘real’ superclass of SING(Fren) and so the class of SING(Fren) is equal to the class of Class. Now Class has the peculiar property of being itself a class and being a class it is also born with a Singleton Class; the class of Class, is therefore, SING(Class).

It follows then, that the class of SING(Fren) is actually SING(Class) – or in notation CLASS(SING(Fren)) = SING(Class). Let’s test this:

puts class_of_meta #output is #<Class:Class>

This result confirms what we got at the start of this article, and yes, contradicts the RHG. According to the RHG the class of a Singleton Class is set to itself yet we have just demonstrated this to be false.

Or is it really false? We still have one more case to examine for rb_make_metaclass(), the case where we create a Singleton Class for a class that is itself a Singleton Class. This case is much less common in practice than the other. Here is how we do it in Ruby:

class Fren; end class << (class << Fren; self; end); end

And here is what rb_make_metaclass() does for this case:

RBASIC(klass)->klass = klass;

From above we can see that class of the Singleton Class is indeed set to itself! This fact somewhat vindicates the RHG (although it is possible RHG was in fact entirely accurate for its particular version of Ruby).

Let’s test this behaviour:

class Fren; end class << (class << Fren; self; end); end puts Fren.actual_class.actual_class #output is #<Class:#<Class:Fren>> puts Fren.actual_class.actual_class.actual_class.actual_class # output is #<Class:#<Class:Fren>>

Yep! The class of the Singleton Class of the Singleton Class is indeed itself. The repercussions of this are quite curious; it means that when an instance method is added to the 2nd level Singleton Class it will at the same time be available as a class method.

Please note what this rather odd behaviour of Singleton Classes of Singleton Classes does NOT imply. It does not imply that every higher level Singleton will set itself as its own class; indeed only the highest (or deepest) level Singleton will set its class to itself – all the lower order Singletons will have their class set to the Singleton of one order higher than their own. That is, if we have a class Fren and 4 levels of Singleton Classes built on top of Fren then we will have the following situation (using parentheses to indicate Singleton Classes and an arrow to indicate the ‘Instance Of’ relationship):

Fren —>(Fren) —> ((Fren)) —> (((Fren))) —> ((((Fren)))) —> ((((Fren)))) —> …

Note that only the 4th level Singleton Class sets itself to its own class.

And finally here is my own Ruby Class diagram, showing the relationships between most of the Classes and their Singleton Classes, as well as illustrating the rather odd higher order Singleton Class behaviour described in this article: (hollow arrow-heads indicate superclass and opaque arrow-heads mean ‘instance of’)

Conclusion

The purpose of this article was to investigate some of the more esoteric aspects of Singleton Classes and to answer specifically the question raised in the RHG – namely “What is the class of a singleton class?”. In answering this question we found that the answer RHG gives is not entirely accurate for Ruby 1.8.6; indeed we found that the rb_make_metaclass() function has different behaviour contingent upon whether the class is a Singleton or just a regular class. We discovered that the higher order Singleton Classes are quite different beasts to our regular Singleton Class and do (in accordance with RHG) set themselves as their own class.

To verify the conclusions stated above for yourself, please download the mult C extension (only tested on Linux). It includes the methods Object#actual_class and Object#actual_super which return the actual class and superclass for the objects they’re invoked on with (almost) no prevarication.

Please note the conclusions stated in this article have only been tested on Ruby 1.8.6 and I am unsure if they hold true for either 1.8.7 or 1.9.

Share this: Twitter

Facebook

Reddit

Like this: Like Loading... Related