Common Lisp the Language, 2nd Edition

Next: Restarts Up: Survey of Concepts Previous: Handling Conditions

## 29.3.4. Object-Oriented Basis of Condition Handling

Of course, the ability of the handler to usefully handle an exceptional situation is related to the quality of the information it is provided. For example, if all errors were signaled by

    (error "some format string")

then the only piece of information that would be accessible to the handler would be an object of type `simple-error` that had a slot containing the format string.

If this were done, `string-equal` would be the preferred way to tell one error from another, and it would be very hard to allow flexibility in the presentation of error messages because existing handlers would tend to be broken by even tiny variations in the wording of an error message. This phenomenon has been the major failing of most error systems previously available in Lisp. It is fundamentally important to decouple the error message string (the human interface) from the objects that formally represent the error state (the program interface). We therefore have the notion of typed conditions, and of formal operations on those conditions that make them inspectable in a structured way.

This object-oriented approach to condition handling has the following important advantages over a text-based approach:

- Conditions are classified according to subtype relationships, making it easy to test for categories of conditions.

- Conditions have named slot values through which parameters are conveyed from the program that signals the condition to the program that handles it.

- Inheritance of methods and slots reduces the amount of explicit specification necessary to achieve various interesting effects.

Some condition types are defined by this document, but the set of condition types is extensible using `define-condition`. Common Lisp condition types are in fact CLOS classes, and condition objects are ordinary CLOS objects; `define-condition` merely provides an abstract interface that is a bit more convenient than `defclass` for defining conditions.

Here, as an example, we define a two-argument function called `divide` that is patterned after the `/` function but does some stylized error checking:

    (defun divide (numerator denominator)
      (cond ((or (not (numberp numerator))
                 (not (numberp denominator)))
             (error "(DIVIDE '~S '~S) - Bad arguments."
                    numerator denominator))
            ((zerop denominator)
             (error 'division-by-zero
                    :operator 'divide
                    :operands (list numerator denominator)))
            (t ...)))

Note that in the first clause we have used `error` with a string argument and in the second clause we have named a particular condition type, `division-by-zero`. In the case of a string argument, the condition type that will be signaled is `simple-error`.

The particular kind of error that is signaled may be important in cases where handlers are active. For example, `simple-error` inherits from type `error`, which in turn inherits from type `condition`. On the other hand, `division-by-zero` inherits from `arithmetic-error`, which inherits from `error`, which inherits from `condition`. So if a handler existed for `arithmetic-error` while a `division-by-zero` condition was signaled, that handler would be tried; however, if a `simple-error` condition were signaled in the same context, the handler for type `arithmetic-error` would not be tried.

Next: Restarts Up: Survey of Concepts Previous: Handling Conditions