How do we talk about classes that do not inherit from the `Class` class in Ruby?

Viewed 82

BasicObject, as far as I have read, is the parent class of all other classes in Ruby. However, it's not until further down in the hierarchy where the Class class is actually defined. As such, I'm having a hard time understanding how BasicObject can be a class at all.

Is the difference here just that there's a difference between a ruby Class in an abstract, object-oriented sense as opposed to classes that are actually instances of Class? Or is the hierarchy actually set up in a way where BasicObject is in fact an instance of Class? I feel pretty sure that's not the case.

If there are two meanings for the word "class" in Ruby, I feel like I lack the language to discuss the two types. If this is indeed the case, what can I call a class that is not an instance of Class?

2 Answers

You can treat all classes are instances of Class in the Ruby world. Worth remembering that everything in ruby is an object, including classes. Yes BasicObject is the top parent class of all classes in Ruby. The class of BasicObject being Class, is not the same as the parent class of BasicObject is Class.

To demonstrate, assume that we have a class Person defined.

  • Person.new.class is Person
  • Person.class is Class
  • Person.superclass is Object
  • Object.class is Class
  • Object.superclass is BasicObject
  • BasicObject.class is Class
  • BasicObject.superclass is nil

Proof of all classes in Ruby are in fact objects, you can get the object id of a class. BasicObject.object_id. In the general object-oriented context, a class of a class shouldn't make sense, unless classes are also objects.

As mind-bending as it sounds, it really is like that. BasicObject is a Class and Class is a BasicObject.

I don't think it's possible to express in ruby, so to make this work MRI direcly creates internal structures for these few fundamental classes and wires them in this cyclical manner.

void
Init_class_hierarchy(void)
{
    rb_cBasicObject = boot_defclass("BasicObject", 0);
    rb_cObject = boot_defclass("Object", rb_cBasicObject);
    rb_gc_register_mark_object(rb_cObject);

    /* resolve class name ASAP for order-independence */
    rb_set_class_path_string(rb_cObject, rb_cObject, rb_fstring_lit("Object"));

    rb_cModule = boot_defclass("Module", rb_cObject);
    rb_cClass =  boot_defclass("Class",  rb_cModule);

    rb_const_set(rb_cObject, rb_intern_const("BasicObject"), rb_cBasicObject);
    RBASIC_SET_CLASS(rb_cClass, rb_cClass);
    RBASIC_SET_CLASS(rb_cModule, rb_cClass);
    RBASIC_SET_CLASS(rb_cObject, rb_cClass);
    RBASIC_SET_CLASS(rb_cBasicObject, rb_cClass);
}

As we can see, there are no two class types, user-facing one and "deep" one. It's the same entity.

Related