The CLOS Metaobject Protocol (MOP) is a semi-standardized reflective extension to CLOS. Most Common Lisp implementations (including CMUCL) implement a metaobject protocol that is similar to the specification given in chapters 5 and 6 of the The Art of the MetaObject Protocol (a book whose title is often abbreviated AMOP). However, the PCL MOP provided by CMUCL has a few differences from AMOP, and behaves differently from the MOP in other Common Lisp implementations in certain respects.
The major issues that can arise are:
- Make sure that you use symbols from the right package. A number
of symbols, such as
STANDARD-CLASSexist both in theCOMMON-LISPpackage and in thePCLpackage. For MOP programming you should be using the symbols from the PCL package. - CMUCL has a package named
MOP, which exports most (but not all) of the symbols defined in AMOP. - Since CMUCL uses special wrappers around class-objects, you
sometimes need to use
PCL::COERCE-TO-PCL-CLASSto coerce the wrapper objects into real MOP-aware objects. For example, this occurs when using theCLASS-OFfunction. - You may need to define methods on
PCL:VALIDATE-SUPERCLASSmore often than is said in AMOP. For example, consider a class calledFOOwhose metaclass isMETA-FOO. Class FOO inherits from class T, whose metaclass isSTANDARD-CLASS, and in the PCL MOP you have to declare that this combination of metaclasses is valid:(defmethod pcl:validate-superclass ((class meta-foo) (super pcl::standard-class)) t)
Details on the class schizophrenia
PCL, the CLOS implementation that is used in CMUCL, is integrated
with the rest of CMUCL in a somewhat incomplete way. The type system
of PCL and of the CMUCL kernel have different notions of what a class
is. This is because classes are fundamental to the CMUCL type system,
yet CMUCL needs to be able to function without PCL loaded (mainly in
order to be able to build itself). The way that this problem is
resolved is by having the CMUCL kernel maintain parallel class
hierarchies. For instance, LISP:CLASS and
PCL:CLASS are different types. The function
LISP:FIND-CLASS returns instances
of LISP:CLASS, whereas the function
PCL:FIND-CLASS returns
PCL:CLASS instances. For example
USER> (lisp:find-class 'cons)
#<built-in-class cons (sealed) {28073C8D}>
USER> (pcl:find-class 'cons)
#<Built-In-Class cons {2817967D}>
These two classes are in one sense the same class, in that they represent the same type: CONS. However, PCL has its own way of representing that type internally.
In order to make this situation livable, PCL has been hacked up to
accept LISP:CLASS objects in the common
places where people supply classes to PCL operations. You can also
explicitly convert between the two kinds of classes, either by using
the class name and the appropriate FIND-CLASS, or by:
(kernel:class-pcl-class lisp-class) ==> the PCL class
(kernel:layout-class (pcl::class-wrapper pcl-class)) ==> the LISP class
Another problem area is with generic functions that are called by PCL
with classes as arguments. These classes will be PCL:CLASSes, so if you try to specialize on e.g.
ALLOCATE-INSTANCE using an
EQL specializer, then make sure
the class in the specializer is a PCL:CLASS.
People who stick to using standard CLOS operations shouldn't ever notice all this smoke and mirrors. People using standard CLOS operations shouldn't have their packages use the PCL package, since the Common-Lisp package exports a consistent set of definitions for standard CLOS operations.
Though the above hacks usually work for simple stuff, they often seem
to break down when defining new metaclasses. What you need to do is
explictly specify the PCL:: prefix on the
class name.