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-CLASS
exist both in theCOMMON-LISP
package and in thePCL
package. 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-CLASS
to coerce the wrapper objects into real MOP-aware objects. For example, this occurs when using theCLASS-OF
function. - You may need to define methods on
PCL:VALIDATE-SUPERCLASS
more often than is said in AMOP. For example, consider a class calledFOO
whose 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:CLASS
es, 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.