Common Lisp the Language, 2nd Edition

Next: Object-Oriented Basis of Up: Survey of Concepts Previous: Trapping Errors

## 29.3.3. Handling Conditions

Blind transfer of control to a `handler-case` is only one possible kind of recovery action that can be taken when a condition is signaled. The low-level mechanism offers great flexibility in how to continue once a condition has been signaled.

The basic idea behind condition handling is that a piece of code called the signaler recognizes and announces the existence of an exceptional situation using `signal` or some function built on `signal` (such as `error`).

The process of signaling involves the search for and invocation of a handler, a piece of code that will attempt to deal appropriately with the situation.

If a handler is found, it may either handle the situation, by performing some non-local transfer of control, or decline to handle it, by failing to perform a non-local transfer of control. If it declines, other handlers are sought.

Since the lexical environment of the signaler might not be available to handlers, a data structure called a condition is created to represent explicitly the relevant state of the situation. A condition either is created explicitly using `make-condition` and then passed to a function such as `signal`, or is created implicitly by a function such as `signal` when given appropriate non-condition arguments.

In order to handle the error, a handler is permitted to use any non-local transfer of control such as `go` to a tag in a `tagbody`, `return` from a `block`, or `throw` to a `catch`. In addition, structured abstractions of these primitives are provided for convenience in exception handling.

A handler can be made dynamically accessible to a program by use of `handler-bind`. For example, to create a handler for a condition of type `arithmetic-error`, one might write:

    (handler-bind ((arithmetic-error handler))body)

The handler is a function of one argument, the condition. If a condition of the designated type is signaled while the body is executing (and there are no intervening handlers), the handler would be invoked on the given condition, allowing it the option of transferring control. For example, one might write a macro that executes a body, returning either its value(s) or the two values `nil` and the condition:

    (defmacro without-arithmetic-errors (&body forms)
      (let ((tag (gensym)))
        `(block ,tag
           (handler-bind ((arithmetic-error
                             #'(lambda (c)     ;Argument c is a condition
                                 (return-from ,tag (values nil c)))))
             ,@body))))

The handler is executed in the dynamic context of the signaler, except that the set of available condition handlers will have been rebound to the value that was active at the time the condition handler was made active. If a handler declines (that is, it does not transfer control), other handlers are sought. If no handler is found and the condition was signaled by `error` or `cerror` (or some function such as `assert` that behaves like these functions), the debugger is entered, still in the dynamic context of the signaler.

Next: Object-Oriented Basis of Up: Survey of Concepts Previous: Trapping Errors