我正在探索Common Lisp语法的一些可能性,并且我想在:around
上创建一个make-instance
方法,以在某些情况下返回一些任意值。为简单起见,当我不传递所需参数时,将其设为nil
。而且可以,但是在调用let时无效:
(defclass foo ()
((bar :initarg := :initform '())))
(defmethod make-instance :around ((type (eql 'foo)) &key =)
(if (not =) nil (call-next-method)))
(print (make-instance 'foo)) ;; => NIL
(print (let ((x (make-instance 'foo))) x)) ;; => #<FOO {10037EEDF3}>
有人可以解释这种情况吗?为什么呢? SBCL是否会尝试变得聪明,或者实际上是一项标准工作?我知道我可以使用apply来解决它:
(print (let ((x (apply #'make-instance (list 'foo)))) x)) ;; => NIL
但是我不想依靠这种解决方法。实际上,我可以为此使用常规函数,这没关系,但是我想了解为什么它会以这种方式工作以及是否可以禁用此行为。
答案 0 :(得分:4)
类似于MAKE-INSTANCE
和SBCL(-> CTOR)中常量类名的优化尝试之一。
这似乎可行:
(defmethod make-instance :around ((class (eql (find-class 'foo)))
&rest initargs
&key =
&allow-other-keys)
(declare (ignorable initargs))
(if (not =) nil (call-next-method)))
但向SBCL专家咨询或提交错误报告可能很有用...
答案 1 :(得分:0)
您的程序不符合要求,因此您观察到的任何结果都可能从一个实现更改为另一个,或者从一个实现版本更改为另一个版本。
请参见Constraints on the COMMON-LISP Package for Conforming Programs,尤其是第19条:
定义一种标准化通用函数的方法,该方法适用于所有参数都是标准化类的直接实例的情况。
元对象协议强加restrictions on portable programs:
可移植程序在指定泛型上定义的任何方法 函数必须至少具有一个既不属于 指定的类,也不是其关联值为 指定类的实例。
在您的情况下,标准通用功能是
MAKE-INSTANCE
,并且eql专门化子的关联值为foo
,是symbol
类的实例。
在(eql (find-class 'foo))
方法中,关联的值是standard-class
的直接实例,因此也不一致。您应该使用自定义元类在make-instance
上定义新方法。
此外,返回:around
的{{1}}方法是另一个问题:
可移植程序可以定义扩展指定方法的方法 除非指定方法的描述明确禁止 这个。除非有相反的明确声明,否则这些 扩展方法必须返回调用返回的任何值 调用下一个方法。
hyperspec说nil
必须返回给定 class 的新 instance ,其中instance被定义为直接实例或间接实例。 direct instance的词汇表条目有一个例句,其中说make-instance
始终返回类的 direct 实例(词汇表条目是规范性的(此处为例句,因此可能会有解释的空间))。但是,返回NIL不符合要求。
感谢SBCL开发人员的时间;参见https://bugs.launchpad.net/sbcl/+bug/1835306