类定型:如何避免创建虚拟实例?

时间:2013-07-29 21:33:29

标签: lisp common-lisp clos mop

我遇到了第三方库需要对类进行操作的问题,就好像它已经完成一样。经过一些阅读后,我理解了这种机制背后的动机,但我真的不知道它是如何运作的。

示例:

(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的实例。我不知道很多细节,但看起来它需要知道为这个类定义的访问器是什么,看起来这些信息在最终确定之前是不可用的。那么,有没有办法回避最终确定问题呢?

该课程不会被延长(至少不会在这个项目中,并且它没有被计划)。创建虚拟实例并没有太大的伤害...它只是一个丑陋的解决方案,所以我希望找到一个更好的解决方案。另外,也许,我会得到更多关于最终确定的信息,这也很好:)

1 个答案:

答案 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))