In this section I will show and discuss the syntactic pattern, which
is used to simulate metaclasses.
For simplicity, I assume that only single inheritance is involved.
More specifically, this chapter will be based on the framework
from section .
A metaclass can be simulated using the following pattern:
(define (metaclass-name)
(let ((self nil)
(super (new-part super-metaclass)))
;; class variables and methods
(let ((class-variable init-value)
...)
(define (class-method formal-parameters...)
body)
...
(define (instance-description)
(let ((super (new-instance-part super-class))
(self nil))
(let ((instance-variable init-value)
...)
(define (instance-method formal-
parameter...)
body)
...
(define (inner-dispatch m)
(cond ((eqv? m selector) method)
...
(else (method-lookup super m))))
(set! self inner-dispatch))
self))
(define (outer-dispatch m)
(cond ((eqv? m 'instantiator) instance-description)
((eqv? m selector) method)
...
(else (method-lookup super m))))
(set! self outer-dispatch))
self))
Although this class pattern is slightly more complicated
than the similar patterns from the previous chapters, the
overall structure of it is quite simple.
Figure illustrates the main components of the
class template from above.
Figure: The main components of a metaclass pattern.
Instances of metaclasses become ``meta-objects'', which represent a class. The instances of classes are described by the so-called instance description, which is a class-like description at the level of the class methods. A class object must return the instance description when it is passed the special message called instantiator . The class variables and the class methods are visible in the instance description. Both the metaclass and the instance description have super
and self variables, the latter of which are bound to inner-dispatch and outer-dispatch respectively.
Super parts of a metaclass can be instantiated by new-part
,
which is described in section .
Super parts of an ``ordinary class'', on the other hand, must be instantiated
by the following primitive.
(define (new-instance-part class) (let ((instantiator (method-lookup class 'instantiator))) (instantiator)))
New-instance-part
simply extracts the instance-description
of the class. This instance description is then instantiated. The
parameter class
refers to the meta-object, which represents the
class. In section it is described how entire (non-meta)
classes can be instantiated.
I will assume that there is exactly one instance of a metaclass:
(define a-class (new-instance metaclass-name))
Given this assumption it might be tempting to define metaclasses directly as ``singular instances'':
(define a-class
(let ((self ...)
(super super-metaclass))
(let ((class-variable init-value)
...)
...)
self))
If, however, metaclasses are to take part in a metaclass hierarchy, the parts of a metaclasses will, following this approach, be shared with other metaclasses. This is probably not what we want. Consequently we will leave it as a discipline only to instantiate metaclasses once.