Class finalization: how to avoid creating dummy instances?
Asked Answered
C

1

5

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 :)

Coeval answered 29/7, 2013 at 21:33 Comment(0)
I
9

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))
Infra answered 30/7, 2013 at 4:13 Comment(2)
Thanks for the answer! I believe optima already imports closer-mop, so that using this function shouldn't be a problem. One more thing though: given my situation, and a "conflict of interests" of sorts. Should optima do the checking and finalization, if it's required, or is it upon the developer to do it? I.e. plainly speaking. Is this an optima bug, or would you generally expect the user of a library to tackle such issues before using the library?Coeval
In this case, I believe this is a bug in optima. My rule of thumb is that if I don't use MOP myself, I should not worry about MOP internals.Infra

© 2022 - 2024 — McMap. All rights reserved.