In order to avoid multiple instantiations of an object part, it is necessary to test for every instantiation of an object part, whether the part already has been instantiated. This section shows how this problem is dealt with.
Let me as the starting point introduce the template of a class with multiple super classes, which uses the precedence list representation of objects.
(define (class-name parts)
(let ((super (super-class-list parts super-class-name ... ))
(self ()))
(let ((instance-variable init-value)
...)
(define (method formal-parameter...)
method-body)
...
(define (dispatch message)
(cond ((eqv? message class-id) class-name)
((eqv? message super-classes) super)
((eqv? message selector) method)
...
(else ())))
(set! self (class-handle dispatch super))
self)))
Overall, a class describes how to instantiate the super parts
of the class. This is programmed in the procedure super-class-list
,
which returns a list of object parts, where each part is
represented as an object precedence list (see and
).
If no sharing of parts is involved, super-class-list
``asks the super class to instantiate itself'' in the normal way (by calling the Scheme procedure underlying the class). If, however, super-class-list finds out that a superclass already has been instantiated once, the object precedence list representation is reconstructed from the existing part and, in turn, its super parts.
In more details, there are a number of new elements in
the class template compared with that from section :
to return the class of which it is an instance. It can be done via the following procedure call: (method-lookup obj class-id) , which at the end of this section will be abstracted to (class-of-object obj) .
at the end of the procedure. Class-handle
returns an object
precedence list when the class is instantiated.
The description of class-handle
is postponed to section
.
Because of the parts parameter, it is necessary to introduce a slightly changed version of the class instantiation primitive, new-instance .
(define (new-instance class) (let ((instance (class ()))) (virtual-operations instance) instance))
In the rest of this section I will describe how the procedure super-class-list , together with the procedures on which it depends, can be implemented. Recall that the purpose of super-class-list is to instantiate the super parts of the actual object part, and to avoid multiple instantiations of the same object part.
The procedure super-class-list is just syntactic sugar for super-class-list-1 , which is more convenient to work with because it has a fixed number of parameters.
(define (super-class-list existing-parts . class-list) (super-class-list-1 existing-parts class-list)) (define (super-class-list-1 existing-parts class-list) (if (null? class-list) () (let ((parts (instantiate-super-class existing-parts (car class-list)))) (cons parts (super-class-list-1 (append parts existing-parts) (cdr class-list))))))
Super-class-list-1 basically maps the procedure instantiate-super-class on each class in class-list . During this mapping, it keeps track of already instantiated parts in its first parameter.
Before it is explained how instantiate-super-class
works, let
us look at a simple example of how the class instantiation proceeds.
The example is based on the class hierarchy shown in figure
(a). The call (new-instance d)
first instantiates the b branch of the class hierarchy. When instantiating the c branch, existing-parts refers to a list of the b-part and the a-part, which are the already existing parts of the object. During the instantiation of the c branch it can be discovered that the a-part shouldn't be instantiated again.
Instantiate-super-class is defined in the following way:
(define (instantiate-super-class existing-parts class) (let ((prev-i (previous-instantiation class existing-parts))) (if prev-i (class-handle prev-i (supers-of-object prev-i)) (class existing-parts))))
It uses the procedure
previous-instantiation
to test whether there exists an
instantiation of class
in existing-parts
.
If no such instantiation exists, class
is instantiated
and returned.
The result of this instantiation is an object precedence list
(see section ).
If class
already has been instantiated, a reconstruction of
the existing object part is returned. At the Scheme level,
prev-i
becomes bound to the dispatch
procedure (self
)
of the already existing instance. In order to reconstruct the object
precedence list representation of the object, I also need information
about the super objects of the object. The list of super parts
(represented as object precedence lists) is returned by the
procedure supers-of-object
.
(define (supers-of-object object) (method-lookup-single-object object super-classes)) (define (method-lookup-single-object object message) (object message))
Based on ``self and super information'', class-handle can reconstruct the precedence list of the already existing object part.
In the procedure previous-instantiation , it is necessary to be able to tell whether a given object is an instance of a given class.
(define (is-class-of? class object) (eq? class (class-of-object object))) (define (class-of-object object) (method-lookup-single-object object class-id))
Is-class-of? uses a function class-of-object , which for a given object returns its class---a lambda expression at the implementation language level. In Scheme, it is possible to compare two procedures for equality using eqv? .
Previous-instantiation can now test if a class already is instantiated in the following way:
(define (previous-instantiation class existing-parts) (let ((res (memb is-class-of? class existing-parts))) (if res (car res) #f)))
If, according to existing-parts , class already has been instantiated it returns the already existing part (which also serves as true.) Else it returns false.
Memb is similar to the Scheme primitive member . Memb tests whether an element is a member of a list using an explicitly passed comparison procedure.
(define (memb comparison obj plist) (cond ((null? plist) #f) ((comparison obj (car plist)) plist) (else (memb comparison obj (cdr plist)))))
Thus, (memb is-class-of? c object-parts) tests whether an object in the list object-parts is an instance of the class c .