为什么在 `let` 中调用 `make-instance` 的方式不同?
Why does calling `make-instance` in `let` work differently?
我正在探索 Common Lisp 语法的一些可能性,我想在 make-instance
上创建一个 :around
方法到 return 在某些情况下的某个任意值。为了简单起见,当我没有传递所需的参数时,让它成为 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
但我不想依赖这种解决方法。实际上,我可以为此使用常规函数,没关系,但我想了解为什么它会这样工作,以及是否可以禁用此行为。
看起来像是对 MAKE-INSTANCE
和 SBCL 中常量 class 名称的优化尝试之一 (-> 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 专家或提交错误报告可能很有用...
您的程序是 non-conforming,因此您观察到的任何结果都可能从一个实现更改为另一个实现,或者从一个实现版本更改为另一个版本。
参见 Constraints on the COMMON-LISP Package for Conforming Programs,特别是第 19 条:
Defining a method for a standardized generic function which is applicable when all of the arguments are direct instances of standardized classes.
meta-object 协议强加 restrictions on portable programs:
Any method defined by a portable program on a specified generic
function must have at least one specializer that is neither a
specified class nor an eql specializer whose associated value is an
instance of a specified class.
在你的例子中,标准化的泛型函数是
MAKE-INSTANCE
,eql specializer 的关联值是 foo
,symbol
class.
的一个实例
在(eql (find-class 'foo))
方法中,关联值是standard-class
的直接实例,所以也是non-conforming;您应该使用自定义元 class 在 make-instance
.
上定义新方法
此外,:around
方法returning nil
是另一个问题:
Portable programs may define methods that extend specified methods
unless the description of the specified method explicitly prohibits
this. Unless there is a specific statement to the contrary, these
extending methods must return whatever value was returned by the call
to call-next-method.
hyperspec 说 make-instance
必须 return 给定 class[=48= 的新 实例 ],其中 instance is defined as either a direct instance or an indirect instance. The glossary entry for direct instance 有一个例句说 make-instance
总是 return a direct a class 的实例(词汇表条目是规范的(这里是例句,所以可能有解释的余地))。然而,返回 NIL 是 non-conforming.
感谢 SBCL 开发人员的宝贵时间;参见 https://bugs.launchpad.net/sbcl/+bug/1835306
我正在探索 Common Lisp 语法的一些可能性,我想在 make-instance
上创建一个 :around
方法到 return 在某些情况下的某个任意值。为了简单起见,当我没有传递所需的参数时,让它成为 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
但我不想依赖这种解决方法。实际上,我可以为此使用常规函数,没关系,但我想了解为什么它会这样工作,以及是否可以禁用此行为。
看起来像是对 MAKE-INSTANCE
和 SBCL 中常量 class 名称的优化尝试之一 (-> 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 专家或提交错误报告可能很有用...
您的程序是 non-conforming,因此您观察到的任何结果都可能从一个实现更改为另一个实现,或者从一个实现版本更改为另一个版本。
参见 Constraints on the COMMON-LISP Package for Conforming Programs,特别是第 19 条:
Defining a method for a standardized generic function which is applicable when all of the arguments are direct instances of standardized classes.
meta-object 协议强加 restrictions on portable programs:
Any method defined by a portable program on a specified generic function must have at least one specializer that is neither a specified class nor an eql specializer whose associated value is an instance of a specified class.
在你的例子中,标准化的泛型函数是
MAKE-INSTANCE
,eql specializer 的关联值是 foo
,symbol
class.
在(eql (find-class 'foo))
方法中,关联值是standard-class
的直接实例,所以也是non-conforming;您应该使用自定义元 class 在 make-instance
.
此外,:around
方法returning nil
是另一个问题:
Portable programs may define methods that extend specified methods unless the description of the specified method explicitly prohibits this. Unless there is a specific statement to the contrary, these extending methods must return whatever value was returned by the call to call-next-method.
hyperspec 说 make-instance
必须 return 给定 class[=48= 的新 实例 ],其中 instance is defined as either a direct instance or an indirect instance. The glossary entry for direct instance 有一个例句说 make-instance
总是 return a direct a class 的实例(词汇表条目是规范的(这里是例句,所以可能有解释的余地))。然而,返回 NIL 是 non-conforming.
感谢 SBCL 开发人员的宝贵时间;参见 https://bugs.launchpad.net/sbcl/+bug/1835306