Contorted-example
works only because the
function named by f
is invoked during the extent of the
exit point.
Once the flow of execution has left the block,
the exit point is disestablished. For example:
(defun invalid-example ()
(let ((y (block here #'(lambda (z) (return-from here z)))))
(if (numberp y) y (funcall y 5))))
One might expect the call (invalid-example)
to produce 5
by the following incorrect reasoning:
let binds y
to the
value of block; this value is a function resulting
from the lambda expression. Because y
is not a number, it is
invoked on the value 5
. The return-from should then
return this value from the
exit point named here
, thereby
exiting from the block again and giving y
the value 5
which, being a number, is then returned as the value of the call
to invalid-example
.
The argument fails only because exit points have dynamic extent. The argument is correct up to the execution of return-from. The execution of return-from should signal an error of type control-error, however, not because it cannot refer to the exit point, but because it does correctly refer to an exit point and that exit point has been disestablished.
A reference by name to a dynamic exit point binding such as a catch tag refers to the most recently established binding of that name that has not been disestablished. For example:
(defun fun1 (x)
(catch 'trap (+ 3 (fun2 x))))
(defun fun2 (y)
(catch 'trap (* 5 (fun3 y))))
(defun fun3 (z)
(throw 'trap z))
Consider the call (fun1 7)
. The result is 10
. At the time
the throw is executed, there are two outstanding catchers with the
name trap
: one established within procedure fun1
, and the other
within procedure fun2
. The latter is the more recent, and so the
value 7
is returned from catch in fun2
.
Viewed from within fun3
, the catch
in fun2
shadows the one in fun1
.
Had fun2
been defined as
(defun fun2 (y)
(catch 'snare (* 5 (fun3 y))))
then the two exit points
would have different names, and therefore the one
in fun1
would not be shadowed. The result would then have been 7
.