📜 ⬆️ ⬇️

Metaclasses in Smalltalk

This article, I hope, will be useful not only for those who want to master Smalltalk, but also for those who want to better understand the problems of building object systems. In Smalltalk, classes are full-fledged objects, and how this is implemented is an excellent example of building a developed system based on several simple principles without undue complication.

(However, I will not deny the connection with the recently published article Metaclasses in Objective-C .)

In order to understand the concept of metaclasses, we need only two principles, from which we draw (almost obvious) logical conclusions.

The first principle is fundamental for object programming as a whole and, in theory, should be the same for any object language (for Smalltalk this is the case anyway):
Everything is an object, that is, it can receive messages and respond to them.
Classes also fall under this “all”. For example, we spawn objects by sending messages to the class whose instance we want to create:
point := Point x: 1 y: 2. 
No supernatural special operations for the miraculous creation of objects, only the sending of messages. In this case, we send the message #x:y: class Point in the hope of getting a point in two coordinates.
')
The second principle is not fundamental for object programming, but in Smalltalk (as in most modern object languages), unfortunately, was also laid down:
The behavior of an object is determined by its class: the class defines a set of methods that describe the reaction of instances to a particular message.
So, all methods for points are defined in the class Point - this is understandable. We can recognize the class of an object by sending it a #class message.
 point class."-> Point" 
But where is the #x:y: method, which describes the processing of the same-name message by the class Point defined?

In order not to complicate the system by introducing some new entities or rules, you can use the existing ones: method #x:y: must be defined in the class instance of which is the class Point . The construction “class of a class” is replaced by the term “metaclass”. Of course, the Point metaclass can be obtained by sending the #class message #class the Point class:
 point class class."-> Point class" Point class."-> Point class" 
As you can see, the Point metaclass is not assigned its own name in the system. Metaclasses are denoted by smalltalk expressions with which they can be obtained.

This circumstance also shows that each class has its own metaclass . We don’t need more than one metaclass per class, but one for each class is also a bit too much. Why do you need so much?

In Smalltalk-76, there was only one metaclass for all classes. But if several classes have the same object as their metaclass, then they have the same behavior, since it is defined in the metaclass. For example, the message #x:y: in this case could be sent to any class: String , Integer , etc. Obviously, this is not very good. The alternative is the complete absence of custom behavior on the side of the class (which is no better), or (even worse) black magic at the level of the language, depending on otherworldly forces, on the level of the language - like in most "modern" mainstream creations.

Therefore, for each class in the system, we need one and exactly one metaclass that determines the behavior of this class. In Smalltalk, a metaclass is automatically created “off-screen” when creating a new class.

Considering that metaclasses (see the first principle) are objects and (see the second principle) are instances of a certain class, we conclude that the above applies to the metaclasses themselves. But there is a peculiarity: in contrast to “ordinary” classes, metaclasses do not need specific behavior - they all store the behavior of their instances in the same way, nothing more is required of them. Therefore, there is no longer any need to create a separate (meta-) metaclass for each metaclass, just one object is enough to determine the behavior of all metaclasses in the system. He was called Metaclass :
 Point class class."-> Metaclass" 
Based on the same principles, and applying the same logic, we conclude that Metaclass should also have a metaclass:
 Metaclass class."-> Metaclass class" 
Please note that metaclass Metaclass did not receive its own name (as unnecessary).

And once again we repeat the past: Metaclass metaclass (like all metaclasses) is Metaclass :
 Metaclass class class."-> Metaclass" 

The chain is closed, that's what we got:
image

We did not touch on the issue of inheritance, but everything is simple here: since the behavior of classes is inherited in the same way as the behavior of objects, the superclass of metaclass C is the metaclass of superclass C:
 Point superclass."-> Object" Point class superclass."-> Object class" Object superclass."-> ProtoObject" Object superclass class."-> ProtoObject class" 
This rule (forced) is violated only where the inheritance chain terminates: ProtoObject does not have a superclass, but its metaclass must be a class:
 ProtoObject superclass."-> nil" ProtoObject class superclass."-> Class" 

In conclusion - a couple of small remarks, not relevant to the point, but interesting.
Metaclasses are not full-fledged classes, since the former do not require all the functionality of the latter:
 Metaclass superclass."-> ClassDescription" 
And for those who want to complete the picture completely, but do not want to run Smalltalk:
 Class superclass."-> ClassDescription" ClassDescription superclass."-> Behavior" Behavior superclass."-> Object" 

Source: https://habr.com/ru/post/207982/


All Articles