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
.