2

Is this a conforming Common Lisp program?

(handler-bind ((condition (let ((x 0))
                            (lambda (c)
                              (declare (ignore c))
                              (print (incf x))))))
  (signal 'condition)
  (signal 'condition))

The output with SBCL (2.0.5.37) is:

1
1

The output with ABCL/CCL/ECL is:

1
2

Which behavior is defined by the Common Lisp standard?


Epilog

This was a bug in SBCL, it is now fixed.

coredump
  • 37,664
  • 5
  • 43
  • 77

1 Answers1

3

It's not exactly clear. The spec says:

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

and then says

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).

If you interpret "run" meaning to call the function, that suggests that the handler expressions are evaluted once, when the bindings are made. This is the CCL/ABCL/ECL/LispWorks implementation, so state is maintained in the closure.

But SBCL appears to have intepreted "run" as meaning "evaluated and called". So a new closure is created each time the handler is run, and state is lost.

I suspect the intent was the first interpretation, since CL has no other "lazy" bindings.

If you change the code in the question to this:

(let ((handler
        (let ((x 0))
          (lambda (c)
            (declare (ignore c))
            (print (incf x))))))
  (handler-bind ((condition handler))
    (signal 'condition)
    (signal 'condition)))

then SBCL behaves in the same way as the other implementations. I think this makes it fairly clear that the interpretation taken by the other implementations is the intended one, and it also provides a practical workaround for what, if that interpretation is in fact correct, is a bug in SBCL.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • sure, since he provided a solution. – Barmar Feb 19 '21 at 20:17
  • 1
    @coredump: I think Barmar's answer is the right one. I wouldn't even have understood the problem if he hadn't answered (`handler-bind` is something I always have to remember how it works). Would it be OK if I merged my workaround into his answer via an edit and then you accepted that combined one? –  Feb 20 '21 at 10:06
  • Yes please, that would be great – coredump Feb 20 '21 at 10:07
  • 1
    @coredump: OK, I've done that, if you are happy with the change & want to accept this combined answer that would be great: I'll nuke mine then as I don't think it serves any purpose. –  Feb 20 '21 at 12:44
  • 2
    @tfb FYI this is now fixed in SBCL (https://github.com/sbcl/sbcl/commit/1074ef41921e195868dd552cedae931a75dffb56) – coredump Feb 21 '21 at 20:43