问题描述
这是一个符合 Common Lisp 的程序吗?
(handler-bind ((condition (let ((x 0))
(lambda (c)
(declare (ignore c))
(print (incf x))))))
(signal 'condition)
(signal 'condition))
SBCL (2.0.5.37) 的输出是:
1
1
1
2
Common Lisp 标准定义了哪些行为?
结语
这是 SBCL 中的一个错误,it is now fixed。
解决方法
不是很清楚。 spec 说:
在指定的处理程序绑定有效的动态环境中执行表单。
然后说
如果找到合适的类型,关联的处理程序将在动态环境中运行,在该环境中这些处理程序绑定都不可见(以避免递归错误)。
如果您将“运行”解释为调用函数,则表明在进行绑定时处理程序表达式会被评估一次。这是 CCL/ABCL/ECL/LispWorks 实现,所以状态在闭包中维护。
但是 SBCL 似乎已经将“运行”解释为“评估和调用”的意思。所以每次运行处理程序时都会创建一个新的闭包,并且状态会丢失。
我怀疑意图是第一种解释,因为 CL 没有其他“惰性”绑定。
如果您将问题中的代码更改为:
(let ((handler
(let ((x 0))
(lambda (c)
(declare (ignore c))
(print (incf x))))))
(handler-bind ((condition handler))
(signal 'condition)
(signal 'condition)))
然后 SBCL 的行为方式与其他实现方式相同。我认为这清楚地表明其他实现所采用的解释是预期的,并且它还提供了一种实用的解决方法,如果该解释实际上是正确的,则是 SBCL 中的错误。