Real Class

It interesting that Ruby, a language which allows you to so easily bypass encapsulation, encapsulates it’s own internals to the point of occasional prevarication. Heres an example…

In my last post I explained how the code above forces the interpreter to assign a newly created Singleton class to the pirate object. If that’s true, how is it possible that the class of a new Object is equal to the class of pirate? It’s not possible, the Ruby interpreter is more or less lying.

rb_obj_class in object.c is the the underlying C function that is accessed when the class message is sent to an object. It’s definition…

rb_obj_class calls the function rb_class_real on the return value of CLASS_OF the recipient of the class message. CLASS_OF is a macro which expands into a call to rb_class_of. I’ll skip the details of rb_class_of, in most cases it simply returns the RBASIC klass pointer, i.e. the struct representing the class of the object.

In the case of the pirate object, the ‘pirate Singleton class is passed to rb_class_real. From there on out, Ruby’s deception is straight forward…

rb_class_real uses a couple of macros to inspect the RBASIC flags of the class, checking if it is a Singleton class or Include class (T_ICLASS). Include classes are special proxy classes that are created when a module is included. I’ll cover them in greater detail in a later post. The relevant thing here, is that Include classes are similar to Singleton classes in that the interpreter considers them an implementation detail which should not be exposed to language consumers (this means you). If the class under inspection is not a Singleton or Include class, it is returned, otherwise the super pointer is followed and the checks are run again. In this way, the interpreter loops through the class hierarchy of the object looking for a “normal” class. For the pirate object, the super pointer for ‘pirate points to Object, and so Object is returned from rb_real_class.

Knowing what the interpreter does, doesn’t answer the question of why it does it. Wouldn’t it be simpler (and more honest) to return the Singleton ‘pirate class when the class message is sent to pirate? There are a couple reasons why Ruby works this way.

Firstly, Matz views Singleton and Include classes as implementation details. Yes, Ruby let’s you add instance specific behavior and mixin modules, but how it does it is not really your business. It’s not an accident that the language does not have a built-in singleton_class method. While this is a reasonable view to have, it’s worth bearing in mind that the existence of class << self as a language construct, more or less releases the Singleton class from Pandora’s box of encapsulation.

There is another more subtle reason for the class method to work this way. As far as I know this was first identified by Kent Beck. Kent wrote a two part article in 1993 for “Smalltalk Report”, “Instance-Specific Behavior: How and Why” and Instance-Specific Behavior: Digitalk Implementation and the Deeper Meaning of it All”. In these articles Kent detailed a pattern for adding instance specific behavior in Smalltalk which is nearly identical to what would later be known as the Singleton class in Ruby. Kent’s pattern notabley does not include anything like rb_real_class.

Two years later, in 1995, Kent published an article for “Smalltalk Report” regrettably entitled, “What, What happened to Garbage Collection?”, in which he pointed out a problem with the Instance-Specific Behavior pattern. Assume that equality for a Point class is defined as below…

Without the existence of something like rb_real_class, two points which share an x and y coordinate will not be equal if one of them has defined a Singleton class.

This is an interesting problem. From a purist’s view, it’s fair to say that they are no longer in fact equal as they are no longer interchangeable. One of the Points is now capable of differing or additional behavior. From a pragmatist’s view however, having equality semantics change because of added instance specific behavior could create nightmarish situations as various frameworks and libraries relying on the original semantic begin to breakdown in subtle and difficult to deduce ways.

Kent’s pragmatic solution is to introduce a realClass method. Singleton classes (actually an instance of Behavior in Smalltalk), return the realClass of their super. Classes return themselves. Object>>class is redefined to send a realClass message to it’s associated class.

This is exactly the same behavior as rb_real_class, albeit implemented in an OO fashion. In fact looking at JRuby, an OO implementation of the Ruby interpreter, we find exactly the same pattern. In JRuby, Singleton classes are represented by instances of MetaClass…

Thanks to my colleague and friend John Hume for his input on this post.

5 Responses to “Real Class”

  1. farooq

    nice post! you do a better at popping the hood than most others, partly because you stick to the point. keep ‘em coming!

  2. pfarley

    Thanks for the endorsement, it’s much appreciated. As long as you’re willing to read ‘em, I’ll keep writing ‘em.

  3. Ryan Davis

    Well… I think you’re possibly misunderstanding singleton classes. EVERY object has a singleton class implicitly, no matter what. So ‘pirate’ is still an Object instance (and points would still be Point instances), even if something has been added to its singleton class. Otherwise, you could take this argument to its logical extreme and say that no instances are _ever_ of the same class.

    I do agree and would enjoy a ruby with a truley open metaobject protocol.

  4. Daniel Berger

    Great post.

  5. pfarley

    Hi Ryan,

    I’m afraid your statement, “EVERY object has a singleton class implicitly, no matter what.” is not accurate. Objects have a singleton class created implicitly the first time the singleton class is accessed, but if it is never accessed, there is no singleton class. Hence the equality issue. More specifically, the singleton class for an object is created the first time rb_singleton_class (line 854 in class.c) is called. I used RubyInline to write an example that shows this in action:

    require “inline”
    class Object
    inline do |builder|
    builder.c ”
    VALUE klass(VALUE object) {
    return RBASIC(object)->klass;
    }”
    end
    end

    class Point; end

    point = Point.new

    klass(point).object_id
    #=> 3080598
    klass(Point.new).object_id
    #=> 3080598
    def point.spin; end
    klass(point).object_id
    #=> 3013818

    What you say is true for classes, a metaclass is created when the class is defined. This is all as of 1.8.6, I haven’t checked if the specifics of this change in 1.9.

    Thanks for reading and your comment. More importantly, thanks for all of your insanely great tools.