Pharo has one simple basic rule: everything is an object. But the objects themselves are not entities living in an abstract universe and do not drink the dew of lilies of the valley for breakfast. They exist in the object memory served by the virtual machine. The virtual machine defines an interface that strictly specifies how the objects have to look like and what rules they need to follow.
For objects, the amount of rules is very low – basically just one. Pharo is a class-based object system that requires every object to have an assigned class. Every object is an instance of some class.
Classic Class rules
For classes, the amount of rules is much wider. Classes are the source of the behaviour of objects, and they shape how the objects will look physically in memory. For this reason, every class needs to be an object with at least three instance variables. The placement and order of these variables are strictly given. These variables are:
superclass– can be
nilfor the class hierarchy roots
methodDict– a dictionary of method name symbols and associated compiled methods. It really needs to be a dictionary, and because this data structure is so essential for the virtual machine, the internal structure of it is strictly defined as well
format– an integer that encodes the kinds and numbers of variables
These three instance variables are defined in the class named
Behavior that is the basic class of all classes in Pharo. If you try to add a new instance variable that will change the order of these three, the virtual machine crashes because it changes the expected layout of the classes in memory. Even if you try to define a superclass of
Behavior and add a new instance variable there, it has the same fatal result because it modifies the memory layout as well.
Classes themselves are objects too, so they need to follow the same rules. Most of the classes have a name and are nicely placed in the Pharo class hierarchy. They are mentioned in the system dictionary (a namespace) and the system browser can work with them. But not all classes need to be like that. Pharo provides an option to create so-called anonymous classes that behave more like standard objects. You can inspect them, but the other development tools do not know about them. They have various usages – mostly in special and strange cases – but they may be handy especially during testing because such classes are not registered in the system, do not need to have a unique name and are very easy to discard. They are created using the message
newAnonymousSubclass that you send to an existing class.
aClass := Object newAnonymousSubclass.
We said that objects are instances of classes and classes are objects. If you think about such rules while drinking something uplifting, you may ask yourself: Is it possible to have an object that is an instance of itself? Can an object be its own class? Does Pharo allow such an edge case? And if yes, is it any useful?
Funky Classes Objects
How to try it? The most straightforward way how to do that would be to create an object (e.g., as an instance of the class
Object) and then assign its class to itself. But here you quickly hit two problems. First, as we said, the virtual machine has some restrictions on the memory layout of classes that you need to follow. The second problem is that Pharo objects have no direct way of how to change their class to another. They do not understand any message like
class: that would do this job.
Surprisingly, both of these problems are quite easily solvable! Let’s see how.
First, we will create an anonymous class. Unlike most of the anonymous classes, we will create classes that have
Class as a superclass (an anonymous metaclass :)). That means that such anonymous class will define the same instance variables as
Class. The three instance variables that make the virtual machine happy plus some other variables that make the tools happy.
aClass := Class newAnonymousSubclass.
Such class looks pretty standard in the Inspector.
Then we create an instance of such a class.
anObject := aClass basicNew.
Inspecting of this class does not offer a very satisfying view. All instance variables are referencing the default value –
nil. Moreover, the print string is broken.
There is an easy fix of this shattering state. We will just copy all instance variables from the original anonymous class.
aClass allInstVarNames do: [ :instVarName | anObject instVarNamed: instVarName put: (aClass instVarNamed: instVarName) ].
Now the object looks the same way as the anonymous class in the Inspector, so there is no need to put the picture here again. They look the same way, but they are not the same objects. And, we still haven’t reached our goal to create an object that is its own class.
(anObject == aClass)
>>> false. (anObject class == anObject)
But we are close. Now, the object is an instance of our anonymous class and the object itself is already quite a suitable class.
(anObject class == aClass)
So we may do the same step that Pharo does when it migrates object to a new class. For example, when you change the structure of the original class object. We may let our anonymous class become the desired object.
aClass becomeForward: anObject.
That updates even the low-level pointer in the object memory that defines a class of our object. It was pointing to the anonymous class that becomes our object. The original anonymous class will stop to exist; we do not need it anymore.
(anObject class == anObject)
The mission accomplished! We have created an object that is its own class.
Now, we can start to play with it. It is a class so we can add methods to it, which can be useful for creating mocks.
anObject compile: 'ultimateAnswer ^ 42'.
And the object will understand it. As the only object in the object memory.
Great, but is it useful? It can be. This technique is not something new. I have seen it in the
It is more an interesting oddity than something that you would use every day in your programs. But it can imagine it may be useful mocking, in some debugging scenarios or modeling programs. Last but not least, it is a nice and amusing feature that demonstrates the flexibility of the object model of Pharo.
Here is the entire code:
aClass := Class newAnonymousSubclass. anObject := aClass basicNew. (aClass allInstVarNames) do: [ :instVarName | anObject instVarNamed: instVarName put: (aClass instVarNamed: instVarName) ]. aClass becomeForward: anObject.
We got some fun around classes, and we hope that you enjoyed it. If you want to understand more conceptually objects, classes and metaclasses you should read the little book: ‘A simple reflective object kernel’ http://books.pharo.org/booklet-ReflectiveCore/ . It is not about Pharo, but it shows in Pharo how to define a minimal reflective kernel, and you can learn a lot of fun aspects of OOP (like instantiation, allocation, lookup,…) in the process.