将处理程序绑定与有状态闭包一起使用是否有效?

Is using handler-bind with a stateful closure valid?

这是一个符合标准的 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

ABCL/CCL/ECL 的输出是:

1
2

Common Lisp 标准定义了哪些行为?


结语

这是 SBCL 中的错误,it is now fixed

不是很清楚。 spec 表示:

Executes forms in a dynamic environment where the indicated handler bindings are in effect.

然后说

If an appropriate type is found, the associated handler is run in a dynamic environment where none of these handler bindings are visible (to avoid recursive errors).

如果您将“运行”解释为调用函数,这表明在进行绑定时处理程序表达式被计算一次。这是 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 中的一个错误。