CLOS方法为何匹配参数类型错误的调用?问题排查
问题根源:CLOS方法参数的类型声明搞反了变量名与类型的位置
你遇到的问题完全不是SBCL的bug,是对CLOS defmethod的参数语法理解出现了偏差。
在CLOS的方法定义里,参数的格式是(变量名 [类型])——第一个是变量名,第二个才是类型限定。你写的:
(defmethod action ((turnstile turnstile) (locked current-state) (i icoin) (msg T)) (warn "1 lock coin unlock ~S" msg))
这段代码的意思是:第二个参数的变量名叫locked,类型限定为current-state,不是说这个参数必须是locked类的实例。同理,第三个方法里的(unlocked current-state)是指变量名叫unlocked,类型是current-state。
这就导致当你调用(action *turnstile* (make-instance 'unlocked) (make-instance 'icoin) :c)时:
- 第二个参数是
unlocked实例,属于current-state的子类,满足第二个方法的类型限定 - 第三个参数是
icoin实例,完全匹配第二个方法的类型
所以CLOS会选择第二个方法执行,而不是触发默认的错误方法。
修正方案
把方法里的参数类型改成你实际想要限定的类(locked或unlocked),变量名可以随便取,比如:
;; 正确限定第二个参数为locked类的实例 (defmethod action ((turnstile turnstile) (state locked) (i icoin) (msg T)) (warn "1 lock coin unlock ~S" msg)) ;; 正确限定第二个参数为unlocked类的实例 (defmethod action ((turnstile turnstile) (state unlocked) (i ipush) (msg T)) (warn "2 unlock push lock ~s" msg))
或者如果你想保留变量名locked/unlocked,就写成:
(defmethod action ((turnstile turnstile) (locked locked) (i icoin) (msg T)) (warn "1 lock coin unlock ~S" msg)) (defmethod action ((turnstile turnstile) (unlocked unlocked) (i ipush) (msg T)) (warn "2 unlock push lock ~s" msg))
这样修改后,调用(action *turnstile* (make-instance 'unlocked) (make-instance 'icoin) :c)就会匹配到默认的错误方法,因为没有任何方法同时满足unlocked类+icoin类的参数组合。
避免此类错误的注意事项
- 牢记CLOS方法参数的语法:
(变量名 类型),类型是变量名后面的那个符号,不要搞反顺序 - 定义方法时,尽量给变量起有意义的名字(比如
state),而不是和类名同名,这样能减少混淆 - 不确定方法的匹配规则时,可以用SBCL的
(describe #'action)查看所有已定义的方法及其参数类型,确认每个方法的限定条件是否符合预期
内容的提问来源于stack exchange,提问作者ruby_object
相关产品推荐
相关产品推荐

