Question

I've run into a problem that a third-party library needs to act on a class as if it was finalized. After some reading I understand the motivation behind this mechanism, but I don't really know how it functions.

Example:

(make-instance 'expression :op '+ :left 'nan :right 'nan)
(defmethod normalize-expression ((this expression))
  (optima:match this
    ((optima::or (expression :left 'nan) (expression :right 'nan)) 'nan)
    ((expression :op op :left x :right y) (funcall op x y))))

Unless I add the first line, the function will not compile, giving me this error:

; caught ERROR:
;   (during macroexpansion of (SB-PCL::%DEFMETHOD-EXPANDER NORMALIZE-EXPRESSION ...))
;   SB-MOP:CLASS-SLOTS called on #<STANDARD-CLASS EXPRESSION>, which is not yet finalized.
;   See also:
;     AMOP, Generic Function SB-MOP:CLASS-SLOTS

optima is a pattern-matching library, the (expression :op op ...) is matching instances of class expression against the given pattern. I don't know in much details, but it looks like it needs to know what are the accessors defined for this class, and it looks like that information is not available until it is finalized. So, is there any way to sidestep the finalization problem?

The class will not be extended (at least not in this project, and it's not being planned). It doesn't hurt that much to create a dummy instance... it is just an ugly solution, so I hoped to find a better one. Also, perhaps, I'd get some more info on finalization, which is good too :)

Was it helpful?

Solution

Forgetting to ensure class finalization seems to be quite common mistake when using MOP.

In lisp, classes are defined in two "phases":

  • Direct class definition
  • Effective class definition

Direct class definition is isomorphic to defclass form. It has class name, names of superclasses, list of direct slots (i.e., slots defined on this particular class but on its superclasses).

Effective class definition contains all information needed for compiler/interpreter. It contains list of all class slots (including those defined on superclasses), class instance layout, references to accessor methods, etc.

Process of transforming direct class definition to effective class definition is called class finalization. Since CLOS supports redefining classes, finalization might be called multiple times for a class. One of the reasons why finalization is delayed is because class may be defined before its superclasses are defined.

Regarding your particular problem: is seems that optima:match should ensure that class is finalized before trying to list its slots. This can be done with two functions: class-finalized-p (to check whether class needs finalization) and finalize-inheritance to actually perform finalization. Or you can use utility function closer-mop:ensure-finalized. (closer-mop is a library for portable usage of CLOS MOP).

E.g.,:

(c2mop:ensure-finalized (find-class 'expression))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top