我遇到了第三方库需要对类进行操作的问题,就好像它已经完成一样。经过一些阅读后,我理解了这种机制背后的动机,但我真的不知道它是如何运作的。
示例:
(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))))
除非我添加第一行,否则该函数将无法编译,给我这个错误:
; 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
是模式匹配库,(expression :op op ...)
匹配给定模式的类expression
的实例。我不知道很多细节,但看起来它需要知道为这个类定义的访问器是什么,看起来这些信息在最终确定之前是不可用的。那么,有没有办法回避最终确定问题呢?
该课程不会被延长(至少不会在这个项目中,并且它没有被计划)。创建虚拟实例并没有太大的伤害...它只是一个丑陋的解决方案,所以我希望找到一个更好的解决方案。另外,也许,我会得到更多关于最终确定的信息,这也很好:)
答案 0 :(得分:7)
在使用MOP时,忘记确保课程定稿似乎是一个很常见的错误。
在lisp中,类分为两个“阶段”:
直接类定义与defclass
形式同构。它有类名,超类名,直接槽列表(即在这个特定类上定义但在其超类上定义的槽)。
有效的类定义包含编译器/解释器所需的所有信息。它包含所有类槽的列表(包括在超类上定义的槽),类实例布局,对访问器方法的引用等。
将直接类定义转换为有效类定义的过程称为类定型。由于CLOS支持重新定义类,因此可能会多次为类调用finalization。终止结束的原因之一是因为可以在定义超类之前定义类。
关于您的特定问题:似乎optima:match
应该确保在尝试列出其插槽之前完成类。这可以通过两个函数完成:class-finalized-p
(检查类是否需要完成)和finalize-inheritance
实际执行完成。或者您可以使用效用函数closer-mop:ensure-finalized
。 (close-mop是一个便携式使用CLOS MOP的库。)
E.g,:
(c2mop:ensure-finalized (find-class 'expression))