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

4 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))))
Run Code Online (Sandbox Code Playgroud)

除非我添加第一行,否则该函数将无法编译,给我这个错误:

; 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
Run Code Online (Sandbox Code Playgroud)

optima是一个模式匹配库,它(expression :op op ...)是匹配expression给定模式的类的实例.我不知道很多细节,但看起来它需要知道为这个类定义的访问器是什么,看起来这些信息在最终确定之前是不可用的.那么,有没有办法回避最终化问题?

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

dmi*_*_vk 7

在使用MOP时,忘记确保类完成似乎是一个很常见的错误.

在lisp中,类定义为两个"阶段":

  • 直接类定义
  • 有效的班级定义

直接类定义是同构的defclass形式.它具有类名,超类名,直接槽列表(即在此特定类上定义但在其超类上定义的槽).

有效的类定义包含编译器/解释器所需的所有信息.它包含所有类槽的列表(包括在超类上定义的槽),类实例布局,对访问器方法的引用等.

将直接类定义转换为有效类定义的过程称为类定型.由于CLOS支持重新定义类,因此可能会多次为类调用finalization.最终化延迟的原因之一是因为可以在定义超类之前定义类.

关于你的特定问题:似乎optima:match应该确保在尝试列出其插槽之前完成类.这可以通过两个函数来完成:( class-finalized-p检查类是否需要完成)并finalize-inheritance实际执行最终化.或者你可以使用效用函数closer-mop:ensure-finalized.(close-mop是一个便携式使用CLOS MOP的库).

例如,:

(c2mop:ensure-finalized (find-class 'expression))
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的回答!我相信 `optima` 已经导入了 `closer-mop`,所以使用这个函数应该不成问题。还有一件事:考虑到我的情况,以及某种“利益冲突”。如果需要,`optima` 是否应该进行检查和最终确定,还是由开发人员来完成?即说白了。这是一个“最佳”错误,还是您通常希望库的用户在使用库之前解决这些问题? (2认同)
  • 在这种情况下,我认为这是 `optima` 中的一个错误。我的经验法则是,如果我自己不使用 MOP,我就不应该担心 MOP 的内部结构。 (2认同)