[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

87-003 MetaObject Protocol

I feel the major lack in this document is a clear statement of how one goes about
defining and implementing a new metaclass. What protocol does a class
need to support in order to be promotable to a metaclass via DEFINE-METACLASS?
Does DEFINE-METACLASS even care, i.e. does it check before promoting a class?
What types of slots? The answer to this question seems to be scattered about 
the document, so this is more a criticism of organization than content. 
An example defining a new metaclass would be helpful, but perhaps this document
is not the place for it, but rather a "User's Manual" type document might
be more appropriate.

As in the programmer interface document, a major technical problem is
lack of specification of compile time semantics. While the 
on pg. 3-14 provides a hook for maintaining information about partially 
defined classes at compile time, no such hook exists for methods. To
give an example of how this can make implementation of a new metaclass
difficult, consider my experience with implementing inheritence of
methods in CommonObjects on CommonLoops (COOL). In COOL, CommonObjects
inheritence is maintained orthogonally to CommonLoops inheritence,
due to the nature of the encapsulation semantics specified by
CommonObjects. But COOL depends on the PCL kernel to obtain information
about what methods are defined on which classes (via CLASS-DIRECT-METHODS).

Where the interaction between these two factors becomes a problem is
in inheritence of methods. COOL determines at compile time what methods
a subclass inherits from its supers since it needs to generate special
code for inherited methods. However, the code generated
by PCL for methods doesn't fully define the method until load time,
as should be the case, since otherwise the compile time environment
could be detrimentally side effected. This means that a CommonObjects
super class and methods on the super class cannot be defined in the 
same file as a subclass, since the superclass methods won't be fully 
defined until load time and therefore won't be found during compilation 
for the subclass to inherit.

To be fair, it would probably be possible to work around this problem
through more extensive redefinition of the method handling portion
of the PCL metaclass kernel, but it seems as if some means of maintaining
partially defined method information, as with classes, could be 
designed to simplify implementing novel kinds of method inheritence.

Additional examples of where compile time semantics may cause problems
appear throughout the document. Consider the following description
of class precedence list calculation on pg. 3-4:

	It is computed (just) before the first instance of this
        class is created, or when any method is defined on this
        class or any subclass.

What effect (if any) does compilation have on the class precedence
list? I can imagine cases where optimization of method lookup may
require knowing the class precedence list at compile time, and
where compilation of something may correspondingly cause the
class precedence list to change. This could even be true for
the default CLOS language; however, I think it is even more
important that compile time semantics get pinned down in the
metaobject protocol because the metaclass kernel is, in a
certain sense, a means of modifying the compiler/evaluator.

As a final point about the importance of nailing down compile
time semantics, consider the possibility of portability problems
developing. This is, in fact, currently the case with DEFSTRUCTs
in Kyoto Common Lisp. Unless *compile time too* mode is turned
on within KCL (the default), the SETF functions for DEFSTRUCT 
accessors don't get generated at compile time, and hence SETF's 
of DEFSTRUCT accessors don't expand properly. Turning on 
*compile time too* mode causes an implicit (EVAL-WHEN (COMPILE) ...) 
to be wrapped around the processing of top level forms, which can 
cause serious problems with embedded languages (like PCL) that require 
certain things (like full method definition) not get done at compile
time but rather only at load time. One solution to this problem
is simply to interpret the file before compiling, but that is
pushing the portability problem back into the system
building process.

Minor points of fuzziness within the document:

1) pg. 3-3. A figure of the inheritence/instance relationship
between the metaclass kernel classes would be most useful 

2) pg 3-6. The description of INITFORM. Is INITFORM run in the
context of a method? Is WITH-SLOTS valid within it? Again, as 
with the CLOS language, this should probably be pinned down 
along with the entire initialization protocol.

3) pg. 3-14. Description of SETF of class name. Does this go
both ways, i.e. if I (SETF CLASS-NAME) then will CLASS-NAMED
recognize the class under the new name? The last time I looked
in PCL, this was not the case (though it may have changed).

4) pg. 3-18. Description of FUNCTION slot in METHOD class.
Is this a fundef object (function pointer) or a symbol
whose function cell is bound? 

5) Section 2.2. The description of the relationship between
the generic function classes and the method classes leaves
me somehow uneasy. While I can't make a case for having
one inherit from the other, it seems as if the relationship
should be more intimate than the generic function classes
simply having a slot for methods. Perhaps this can be
handled by  using inheritence from one of the
generic function classes and having the SETF method
distinguish which methods get put onto the slot. I am
particularly concerned about having two co-existing 
metaclasses and users trying to define methods on the
same symbol from both metaclasses. We currently handle
this in COOL by convention, i.e. telling users  to
use PCL and COOL in the same package at their own risk.

6) pg. 3-21, and throughout the discussion of the
generic function classes. I somehow have the feeling that
having the generic function classes inherit from a
standard type class FUNCTION would more elegently tie
together the CLOS with Common Lisp, provided, of course,
the questions raised by the discussion on having a class
for FUCTION and the other types which were originally
not on the list to have classes can be answered.