Demystifying Ruby Singleton Classes
One of the most mysterious aspects of the Ruby object model is the existence of singleton classes. Maybe you also read about metaclasses or eigenclasses, they are actually all referring to the same thing. Still, the "official" name being singleton classes, I will use this term through this post.
You actually don't need to understand much of singleton classes (or to even know them at all) to write decent Ruby code. Though, as they form an essential part of the Ruby object model, it is an interesting knowledge to have.
The method dispatch problem
First, to fully understand the purpose of such classes, we have to look to another essential piece of Ruby: method dispatching. Consider the following code:
class Vehicle
def initialize(kms)
@kms = kms
end
def drive
puts "let's go!"
end
end
car = Vehicle.new(20_000)
car.drive
Nothing fancy here. We define a class Vehicle with a method drive
and then create a new instance of it. Let’s dig into what these few lines of code actually do behind the scenes.
Internally, here is how Ruby is representing the class Vehicle
and its instance car
in memory:
The car
object is represented by Ruby using a C struct, holding the list of its instance variables and a pointer klass
to the its class. The class Vehicle
itself holds its list of methods in and a super
attribute pointing at its superclass (in our case, Object
, the default superclass for all classes).
As we can see, the car
object has actually no idea that it has a method named drive
. All it knows is that it owns an instance variable named kms
. When we call the method drive
on the object, Ruby will actually follow the klass
pointer of the underlying C struct to find the object's class. In the method list of this class, it will find the method drive
and then call it on the current value of self
, which is car
.
As classes are also objects in Ruby, a similar strategy is used to resolve a method called on a class. Let's go back to the previous example and add some code:
class Vehicle
@@registry = []
def self.register(vehicle)
@@registry << vehicle
end
# ...
end
car = Vehicle.new(20_000)
Vehicle.register(car)
We just defined the class method register
on our Vehicle
class to manage a simple list of registered vehicles. Good. As we said, method dispatch works in the exact same way for classes than for objects. That means, calling Vehicle.register
should work as we saw before, but with the Vehicle
class instead of its instance.
Here is a schema similar to the one we saw earlier, showing the Vehicle
class:
So, our class Vehicle
owns an instance variable registry
and its klass
pointer is referencing the class of all classes, Class
(which defines the class method new
for example).
But then, where is the class method register
? It can't be into the method list Vehicle
. As we saw before, due to the way the Ruby object model works, it can only contains instance methods.
So, since method dispatch must work the same as for all other objects, Ruby should follow the klass
pointer of Vehicle
to find the method. However, this would mean that the register
method is defined as a method of Class
, which would mean all of our classes would get this method. So, neither of these solutions can work.
Ruby solves this problem using singleton classes.
Discovering singleton classes
A singleton class of an object (or a class) is a class created by Ruby only for this specific object. This class is somehow "hidden" to us, but it is there. When calling a method on this object, Ruby will look first into its singleton class, if there is one, to find that method.
For the rest of this post, we will refer to the singleton class of a class A
as #A
(and to the one of an object a
as #a
). So, taking back our example of the Vehicle
class, here is how things look when we define the register
class method:
The schema above is actually slightly simplified, as we will see in a few minutes. But for now let's focus of what is happening here. Writing def self.register
, we actually tell Ruby to open the singleton class of self
(which is then pointing to the Vehicle
class) and define the register
method on it.
Now, when we call Vehicle.register
, Ruby will look first into #Vehicle
instead of Class
, to find the register
method, calling it with the value of self
being the class Vehicle
. Since the super
of #Vehicle
is pointing to Class
, Ruby can still follow the chain to find any other class method like new
. We are back on our feet.
Singleton classes can be defined in the exact same way for "regular" objects. We could then define a method only on a specific Vehicle
instance:
car = Vehicle.new(20_000)
def car.start
puts "starting..."
end
We open the singleton class of car
and define a start
method on it. This method is then only available for this specific object. Again, we can draw a similar schema showing the relation between car
, its singleton class and the Vehicle
class.
Move along, no class here
Remember when we said that singleton classes where hidden to us. For example, calling class
on the object will not return its singleton class #car
as we could expect, but its class Vehicle
. That is because internally, Ruby uses a special flag on the singleton class. Also, it cannot be instantiated like a regular class.
However, as the super
attribute of #car
is pointing to the original class Vehicle
, it is possible to override methods defined by Vehicle
calling super
:
car = Vehicle.new(1000)
bus = Vehicle.new(3000)
def car.drive
print "I'm driving a car! "
super
end
car.drive # "I'm driving a car! let's go!"
bus.drive # "let's go!"
We can have access to the singleton class by calling the singleton_class
method on the car
object. By inspecting the singleton class, we can see Ruby names it using the following pattern: #<Class:<original_object>>
> car.inspect
=> "#<Vehicle:0x007fd4442c38f8>"
> car.singleton_class.inspect
=> "#<Class:#<Vehicle:0x007fd4442c38f8>>"
This is also true for the singleton class of a class:
> Vehicle.inspect
=> "Vehicle"
> Vehicle.singleton_class.inspect
=> "#<Class:Vehicle>"
The truth about Singleton Classes of Classes
So far, we learned that the class method register
is actually defined on the singleton class of Vehicle
, #Vehicle
. But now let's take a slightly more complicated example:
class Vehicle
@@registry = []
def self.register(vehicle)
@@registry << vehicle
end
end
class Car < Vehicle
def self.register(car)
# register car plate
super
end
end
some_car = Car.new
Car.register(some_car)
We created the class Car
which is a subclass of Vehicle
. We define as well a class method register
on Car
that does some car-specific work and calls the register
method on the parent class Vehicle
using super
. If we take the time to draw again a schema using what we know, here how it looks:
There is something that we miss here. The Car
class has a class method register
on its singleton class #Car
that clearly calls the register
method of its superclass, Vehicle
. But we saw that this method is itself defined on #Vehicle
. If #Car
and #Vehicle
have no relationship, how can Ruby get back on his feet to find the register
method of the superclass?
That's where the magic of the Ruby object model reveals itself. Here is the ultimate schema where we can see what are the real relationships between all the elements we looked at so far:
Take some time to analyze and understand this one, because for sure, it can be quite complicated and weird to get the first time. What happens is that, for the singleton class #Car
to be able to call the superclass's method register
defined on the singleton class #Vehicle
, Ruby actually make the super
pointer of #Car
points to #Vehicle
instead of class. That's why we are able to call a superclass class method from a subclass class method. In the end, even the klass
pointer of the Object
class points to #Object
(that's why can also define class methods on the Object
class).
Finally, #Object
's super
pointer goes back to Class
. So, as all Ruby classes inherit from Object
, all of their singleton classes inherits from #Object
and ultimately from Class
.
From these last learnings, we can infer two rules that are part of the core principles of the entire Ruby object model:
The superclass of the singleton class of an object is the object’s class.
And
The superclass of the singleton class of a class is the singleton class of the class’s superclass.
Yep. Read it again a second time and a third time, just in case.
Alternative syntaxes
There are other ways to define methods on the singleton class of an object. We can use the special class <<
syntax to directly open the singleton class and define methods on it:
car = Vehicle.new(2000)
class << car
def start
"starting..."
end
end
Again, this is also true for classes, so you can also use this syntax to define class methods (you can see it a lot in some frameworks and libraries like Rails):
class Vehicle
class << self
def register(vehicle)
@@vehicles << vehicle
end
end
end
Another way of defining methods on the singleton class is to use a module and the extend
method. You may have already used extend
. What it actually does is opening the singleton class of the receiver object and add methods from the module passed in argument.