Node:Lazy Catch, Next:Exception Implementation, Previous:Throw, Up:Exceptions
A lazy catch is used in the same way as a normal catch
,
with key, thunk and handler arguments specifying the
exception type, normal case code and handler procedure, but differs in
one important respect: the handler procedure is executed without
unwinding the call stack from the context of the throw
expression
that caused the handler to be invoked.
lazy-catch key thunk handler | Scheme Procedure |
scm_lazy_catch (key, thunk, handler) | C Function |
This behaves exactly like catch , except that it does
not unwind the stack before invoking handler.
The handler procedure is not allowed to return:
it must throw to another catch, or otherwise exit non-locally.
|
Typically, handler should save any desired state associated with
the stack at the point where the corresponding throw
occurred,
and then throw an exception itself -- usually the same exception as the
one it caught. If handler is invoked and does not throw an
exception, Guile itself throws an exception with key misc-error
.
Not unwinding the stack means that throwing an exception that is caught
by a lazy-catch
is almost equivalent to calling the
lazy-catch
's handler inline instead of each throw
, and
then omitting the surrounding lazy-catch
. In other words,
(lazy-catch 'key (lambda () ... (throw 'key args ...) ...) handler)
is almost equivalent to
((lambda () ... (handler 'key args ...) ...))
But why only almost? The difference is that with
lazy-catch
(as with normal catch
), the dynamic context is
unwound back to just outside the lazy-catch
expression before
invoking the handler. (For an introduction to what is meant by dynamic
context, See Dynamic Wind.)
Then, when the handler itself throws an exception, that exception
must be caught by some kind of catch
(including perhaps another
lazy-catch
) higher up the call stack.
The dynamic context also includes with-fluids
blocks (REFFIXME),
so the effect of unwinding the dynamic context can also be seen in fluid
variable values. This is illustrated by the following code, in which
the normal case thunk uses with-fluids
to temporarily change the
value of a fluid:
(define f (make-fluid)) (fluid-set! f "top level value") (define (handler . args) (cons (fluid-ref f) args)) (lazy-catch 'foo (lambda () (with-fluids ((f "local value")) (throw 'foo))) handler) => ("top level value" foo) ((lambda () (with-fluids ((f "local value")) (handler 'foo)))) => ("local value" foo)
In the lazy-catch
version, the unwinding of dynamic context
restores f
to its value outside the with-fluids
block
before the handler is invoked, so the handler's (fluid-ref f)
returns the external value.
lazy-catch
is useful because it permits the implementation of
debuggers and other reflective programming tools that need to access the
state of the call stack at the exact point where an exception or an
error is thrown. For an example of this, see REFFIXME:stack-catch.