Ruby Object Model

Data structure in detail

Comparison with Smalltalk-80

As an appendix to [], a description of the Smalltalk-80 object model is presented. At present, only the structure correspondent to Ruby's S1-structure (built by superclass and eigenclass links) is provided.
Author
Ondřej Pavlata
Jablonec nad Nisou
Czech Republic
Document date
Initial releaseFebruary 10, 2012
Last major release February 10, 2012
Last updateJune 21, 2012
Warning
  1. This document has been created without any prepublication review except those made by the author himself.
Tested Smalltalk implementations
Two major implementations of Smalltalk-80 have been taken into consideration:

Table of contents

 

The class–metaclass dialectics

Despite that Smalltalk was created as a pure object-oriented programming language in part for educational use, the concept of its classes and metaclasses has not been clearly established.
The terminological ambiguity
No convention has been established as to which objects are classes and which are metaclasses. More specifically, there is no standard terminology (known to the author) that clearly states whether As a result, terminological paradoxes arise, depending on which of (A) or (B) is assumed.
(A) metaclasses classes
The inclusion assumption is in correspondence with the (presumably) standard definition: A metaclass is a class whose instances are classes [] [] (*) or Metaclasses are classes of classes. Unfortunately, this is in contradiction with the following statements about the Smalltalk-80 object model:

(*) Note that this definition is incorrect: It implies that all non-instantiable classes are metaclasses.

(B) metaclasses classes == ∅
The mutual exclusion assumption is in correspondence with the terminology we have introduced for the Ruby object model. When applied to the Smalltalk-80 object model, the following statement becomes contradictory: In this statement, the term the-class-of corresponds to the map between objects. This map is established by a method called class. (Using the Smalltalk syntax, the value for an object x is obtained by x class.) In fact, if (B) is assumed, then the semantics of the Smalltalk class method yields the opposite of (): This can be called the class map paradox.
The broken one-to-one correspondence
Despite that authoritative publications proclaim that metaclasses are in one-to-one correspondence with classes [] or that each class is an instance of its own metaclass [], both Smalltalk's two major implementations, Pharo and Squeak, allow counter-examples to these statements. The following are the two main counter-examples (note that they are not based on the terminological ambiguity):

The broken inheritance hierarchy

As of Pharo 1.3 and Squeak 4.2, the Smalltalk's class inheritance defined by superclass links is neither acyclic nor single rooted in the general case.
Inheritance cycles
The superclass: method allows creation of cycles. For classes X, Y, the code
Multiple roots
Both Pharo 1.3 and Squeak 4.2 have multi-rooted built-in class hierarchy. In addition to the ProtoObject class, there is at least one other class x such that x superclass == nil: Interestingly, in both cases the additional root x satisfies the twist condition (just like ProtoObject), namely that

Handling the issues

Ruby nomenclature adoption
In order to avoid terminological paradoxes, we adopt the nomenclature of Ruby objects from []. However, we shorten the term implicit metaclass to metaclass where the adjective implicit is present in the tooltip. This allows us to assume (B) – i.e. We solve the class map paradox (†) by a change in terminology: We will use actualclass instead of class. Specifically: The use of the actualclass/aclass terminology and notation is deliberate. It is because the Smalltalk's the-class-of map corresponds to Ruby's actualclass map rather than to the Ruby's class map.

We also perform the following preparation for a rubyfication of Smalltalk's object model:

Instantiation restrictions
We restrict the Smalltalk object model to structures satisfying the one-to-one correspondence. This means that we have to impose restrictions to object instantiation. In particular, we make the following assumption:
Subclassing restrictions
We also disallow subclassing of Class and Metaclass, i.e. we assume that
Ruling out cycles
We make the assumption that superclass links are preserved - they cannot be changed by transitions.
Ruling out multiple roots
We deny the existence of additional inheritance roots like PseudoContext or ObjectTracer.

 

SmT0: The 2x2 nomenclature

An SmT0 structure is a structure (Oa, .terminative?, .primary?) where The structure is subject to the following condition:
(SmT0~1) All terminative actual objects are primary, i.e. there are no terminative eigenclasses in Oa.
As a consequence, we can use the word terminal instead of terminative actual object.

Using the 2x2 Ruby nomenclature from [], the structure can be diagrammatized as follows. In particular, the set Oa is partitioned into terminals, classes and metaclasses.

primordiality →
Primary
objects
 
First
eigenclasses
 
 terminality ↓
Classive objects
alias
Classives
Classes
Metaclasses
Terminative objects
alias
Terminatives
Terminals
Eigenclasses
of terminals

Note:

SmT1: Inheritance and the actualclass pseudotree

Note: An alternative title of this section is: The superclass and actualclass maps.

An SmT1 structure is an SmT0 structure equipped with (.sc, .aclass, r, Metaclass) where Similarly to Ruby's S1, additional notation / terminology applies. The structure is subject to the following axioms:
(SmT1~1) The inheritance is an algebraic tree on non-terminals, its root is r.
We denote by x.hancs the list of sc-inheritance ancestors of x, starting with x and ending with r. We also let x.hancestors be x.hancs without metaclasses.
(SmT1~2) For the actualclass map .aclass the following hold:
  • Terminals are mapped to classes.
  • Classes are bijectively mapped to metaclasses – i.e. .aclass establishes a one-to-one correspondence between classes and metaclasses.
  • Metaclasses are constantly mapped to the Metaclass object.
For the Metaclass object the following holds:
  • The Metaclass object is a class. (In particular, it is not a metaclass.)
(SmT1~3) .sc and .aclass are commutative in the following sense:
If x is a class, different from r, then x.aclass.sc is defined and is equal to x.sc.ec.
(Using (SmT1~4): The .sc-inheritance on metaclasses is parallel to the .sc-inheritance on classes.)
(SmT1~4) .sc preserves being a class.
(SmT1~5) No correspondent to Ruby's (S1~5)
(SmT1~6) No correspondent to Ruby's (S1~6)
(SmT1~7) The list r.aclass.sc.hancs contains exactly n classes, called helix classes, where
  • n == 5, as of Pharo 1.3 or Squeak 4.2, which corresponds to the chain
    Class < ClassDescription < Behavior < Object < ProtoObject.
In particular, n is at least 2.
(SmT1~8)
  • Only metaclasses x satisfy x.aclass == Metaclass.
  • No object x satisfies x.aclass == Class.
  • No object x satisfies x.sc == Metaclass.
  • r.aclass is the only object x satisfying x.sc == Class.
(SmT1~9) Metaclass and Class are siblings in the sc-inheritance, i.e.
  • Metaclass.sc == Class.sc.
The actualclass pseudotree

Proposition:

As a consequence, the structure (Oa, .aclass) is a pseudotree with the pseudoroot being the 2-element set { Metaclass, Metaclass.aclass }. We call this pseudotree the actualclass pseudotree. Its levels are described by the following table.
level depth level members
0 (top level) { Metaclass, Metaclass.aclass }
1 Metaclasses except Metaclass.aclass
2 Classes except Metaclass
3 Terminals
The structure can be then diagrammatized as follows.
Legend
  • … the Metaclass class
  • … the Class.aclass metaclass
  • … the constant map from metaclasses to the Metaclass class
  • … the one-to-one map from classes to metaclasses
  • … the many-to-one map from terminals to classes

Note: The Ruby's actualclass map is obtained by redirecting blue arrows to .

The real class map
In a correspondence to the Ruby's .class map, the Smalltalk's real class map, denoted .rclass, maps objects to classes. It is defined as follows:

Proposition:

  1. (1) For every object x, x.rclass equals the first member of the ancestor list x.aclass.hancs that is a class, i.e. x.rclass == x.aclass.hancestors[0].
  2. (2) The structure (Oa, .rclass, Class) is an algebraic tree of depth 2.
Note that both (1) and (2) are valid in Ruby (with .rclass replaced by .class and Oa replaced by O). The only difference is that the real class of all Smalltalk's metaclasses is the Metaclass class whereas in Ruby, the class of all eigenclasses is the Class class.

The structure (Oa, .rclass, Class) can be diagrammatized as follows.

Legend
  • … the Class class     … the Metaclass class
  • … classes except the Class class
  • … terminals     metaclasses
  • … the constant map from classes to the Class class
  • … the constant map from metaclasses to the Metaclass class
  • … the many-to-one map from terminals to classes

Note: The Ruby's class map is obtained by redirecting blue arrows to .

The kind-of relation
The kind-of relation is defined between objects as the composition .aclass ◦ . Equivalently,
The instance-of relation
We define the instance-of relation as the composition .rclass ◦ . Equivalently, If x.rclass == y then we say that x is a direct-instance-of y.

Proposition:

Note:

We deviate here from the common definition in Smalltalk which states that the instance-of relation is exactly the actualclass pseudotree, i.e. x is an instance of y iff x.aclass == y. Our definition yields the following advantages:
Similarly to Ruby, if A is a class, then by As we mean the set of all instances of A.

The following proposition shows that there is at least one advantage of the Smalltalk object model over the Ruby object model.

Proposition:

  1. The set of all classes   equals the set of all Classes.
  2. The set of all metaclasses equals the set of all Metaclasses.

SmT1R: Rubyfication of SmT1 (up to first eigenclasses)

The Rubyfication of SmT1 is performed by adding fictitious eigenclasses of terminals. This can be schematized by
  
  
       
  
  
An SmT1R structure is an SmT1 structure extended with (O₀₁, .ec) where The structure is subject to the following condition:
(SmT1R~1) In the restriction to classes, the eigenclass map .ec coincides with the actualclass map .aclass.

We denote .ce the inverse of .ec and extend the following maps to non-actual objects x:

Proposition A:

  1. (O₀₁, .sc, .ec) is a Ruby S1₀₁ structure   (up to the number of helix classes).
    This in particular induces the Ruby's class map, .class, restricted to O₀₁.
  2. The set Oa defines an actuality (corresponding to an indicator function .actual?) that has Smalltalk extent.
    This in particular induces the Ruby's actualclass map, .aclass, restricted to O₀₁.

Proposition B:

  1. Except for metaclasses, the .rclass map coincides with Ruby's .class map. Specifically,
  2. Except for metaclasses, the .aclass map coincides with Ruby's .aclass. Specifically,

Correspondence table

Terminology Smalltalk Ruby
Smalltalk expression Our expression Our expression Ruby expression
the superclass of a non-terminal x x superclass x.sc x.superclass
the eigenclass of x
x.ec x.singleton_class (limited use)
the actualclass / actualclass of x x class x.aclass x.aclass
the (real) class of x
x class class == Metaclass
  ifTrue:  Class
  ifFalse: x class
x.rclass x.class x.class
the class side of a metaclass x (†) x thisClass x.ce Internally, x.ce is referenced by an internal instance variable __attached__
inheritance ancestors of a non-terminal x {x}, x.allSuperclasses x.hancs Obtainable by following .superclass links.
classes that are inheritance ancestors of x Obtainable by
removing metaclasses from {x}, x.allSuperclasses
x.hancestors x.ancestors - x.included_modules
x's actualclass / actualclass is y? x isMemberOf: y x.aclass == y x.aclass == y
x direct-instance-of y?
x class class == Metaclass
  ifTrue:  y == Class
  ifFalse: x class == y
x.rclass == y x.class == y x.class == y
x instance-of y?
y class class == Metaclass
& x isKindOf: y
(x,y) .rclass ◦ (x,y) .class ◦ x.class ≤ y && y.class == Class
x kind-of y? x isKindOf: y (x,y) .aclass ◦ (x,y) .aclass ◦ x.kind_of? y && y.kind_of? Class (*)
is x terminal?
x class class ~= Metaclass
& x class ~= Metaclass
x.class != Class
is x a class?
x class class == Metaclass
x.class == Class && x == x.ancestors[0]
is x a metaclass?
x class == Metaclass
Class == x.class && !!(Class > x) (**)

Notes:

The metaclass term revisited

We have renamed the Smalltalk's the-class-of map to the actualclass map. This change in terminology can be justified by the argument that
a real class map should be a many-to-one mapping without any part that has enforced one-to-one characteristics.
Therefore, we consider the parallel green arrows in the actualclass pseudotree as an indication that the Smalltalk's the-class-of map is NOT a real class map. Instead, we consider the real class map to be .rclass, which is the Ruby's class map except for the value on metaclasses.

In .rclass, classes are mapped constantly to the Class class, so that they are not mapped to metaclasses. This means that

implicit metaclasses are NOT classes-of classes.
Thus, our class-of map is terminologically consistent with our assumption that a metaclass is not a class.

The standard definition of a metaclass [] [] applies to explicit metaclasses:

Explicit metaclasses are classes of classes.
According to this definition, both Ruby and Smalltalk-80 (as well as Smalltalk-76) contain exactly one explicit metaclass: the Class class. This also means that the Smalltalk's Metaclass class is neither explicit nor implicit metaclass.

 

References
Mohamed Dahchour, Alain Pirotte, Esteban Zimányi, Definition and Application of Metaclasses, Proceedings of the 12th International Conference on Database and Expert Systems Applications, Springer-Verlag 2001, http://cs.ulb.ac.be/publications/P-01-01.pdf
Ira R. Forman, Scott H. Danforth, Putting Metaclasses to Work, Addison Wesley 1998
Adele Goldberg, David Robson, Smalltalk-80: The Language and Its Implementation, Addison Wesley 1983, http://stephane.ducasse.free.fr/FreeBooks/BlueBook/Bluebook.pdf
John Hunt, Smalltalk and Object Orientation: An Introduction, Springer Verlag 1997, http://stephane.ducasse.free.fr/FreeBooks/STandOO/Smalltalk-and-OO.pdf
Ondřej Pavlata, The Ruby Object Model: Data Structure in Detail, 2012, http://www.atalon.cz/rb-om/ruby-object-model
Wikipedia: The Free Encyclopedia, http://wikipedia.org
Document history
February102012 The initial release.
April52012 Minor additions and corrections.
June212012 Enhanced terminology for metaclasses (distinguishing between explicit a implicit).
License
This document is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License.