Next: , Previous: , Up: Exceptions   [Contents][Index]


6.14.8.3 Throw Handlers

It’s sometimes useful to be able to intercept an exception that is being thrown before the stack is unwound. This could be to clean up some related state, to print a backtrace, or to pass information about the exception to a debugger, for example. The with-throw-handler procedure provides a way to do this.

Scheme Procedure: with-throw-handler key thunk handler
C Function: scm_with_throw_handler (key, thunk, handler)

Add handler to the dynamic context as a throw handler for key key, then invoke thunk.

This behaves exactly like catch, except that it does not unwind the stack before invoking handler. If the handler procedure returns normally, Guile rethrows the same exception again to the next innermost catch or throw handler. handler may exit nonlocally, of course, via an explicit throw or via invoking a continuation.

Typically handler is used to display a backtrace of the stack at the point where the corresponding throw occurred, or to save off this information for possible display later.

Not unwinding the stack means that throwing an exception that is handled via a throw handler is equivalent to calling the throw handler handler inline instead of each throw, and then omitting the surrounding with-throw-handler. In other words,

(with-throw-handler 'key
  (lambda () … (throw 'key args …) …)
  handler)

is mostly equivalent to

((lambda () … (handler 'key args …) …))

In particular, the dynamic context when handler is invoked is that of the site where throw is called. The examples are not quite equivalent, because the body of a with-throw-handler is not in tail position with respect to the with-throw-handler, and if handler exits normally, Guile arranges to rethrow the error, but hopefully the intention is clear. (For an introduction to what is meant by dynamic context, See Dynamic Wind.)

C Function: SCM scm_c_with_throw_handler (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data, int lazy_catch_p)

The above scm_with_throw_handler takes Scheme procedures as body (thunk) and handler arguments. scm_c_with_throw_handler is an equivalent taking C functions. See scm_c_catch (see Catch) for a description of the parameters, the behaviour however of course follows with-throw-handler.

If thunk throws an exception, Guile handles that exception by invoking the innermost catch or throw handler whose key matches that of the exception. When the innermost thing is a throw handler, Guile calls the specified handler procedure using (apply handler key args). The handler procedure may either return normally or exit non-locally. If it returns normally, Guile passes the exception on to the next innermost catch or throw handler. If it exits non-locally, that exit determines the continuation.

The behaviour of a throw handler is very similar to that of a catch expression’s optional pre-unwind handler. In particular, a throw handler’s handler procedure is invoked in the exact dynamic context of the throw expression, just as a pre-unwind handler is. with-throw-handler may be seen as a half-catch: it does everything that a catch would do until the point where catch would start unwinding the stack and dynamic context, but then it rethrows to the next innermost catch or throw handler instead.

Note also that since the dynamic context is not unwound, if a with-throw-handler handler throws to a key that does not match the with-throw-handler expression’s key, the new throw may be handled by a catch or throw handler that is closer to the throw than the first with-throw-handler.

Here is an example to illustrate this behavior:

(catch 'a
  (lambda ()
    (with-throw-handler 'b
      (lambda ()
        (catch 'a
          (lambda ()
            (throw 'b))
          inner-handler))
      (lambda (key . args)
        (throw 'a))))
  outer-handler)

This code will call inner-handler and then continue with the continuation of the inner catch.


Next: , Previous: , Up: Exceptions   [Contents][Index]