Common Lisp the Language, 2nd Edition

Next: Trapping Errors Up: Survey of Concepts Previous: Survey of Concepts

## 29.3.1. Signaling Errors

Conceptually, signaling an error in a program is an admission by that program that it does not know how to continue and requires external intervention. Once an error is signaled, any decision about how to continue must come from the ``outside.''

The simplest way to signal an error is to use the `error` function with `format`-style arguments describing the error for the sake of the user interface. If `error` is called and there are no active handlers (described in sections 29.3.2 and 29.3.3), the debugger will be entered and the error message will be typed out. For example:

    Lisp> (defun factorial (x)
            (cond ((or (not (typep x 'integer)) (minusp x))
                   (error "~S is not a valid argument to FACTORIAL."
                          x))
                  ((zerop x) 1)
                  (t (* x (factorial (- x 1))))))
     => FACTORIAL
    Lisp> (factorial 20)
     => 2432902008176640000
    Lisp> (factorial -1)
    Error: -1 is not a valid argument to FACTORIAL.
    To continue, type :CONTINUE followed by an option number:
     1: Return to Lisp Toplevel.
    Debug>

In general, a call to `error` cannot directly return. Unless special work has been done to override this behavior, the debugger will be entered and there will be no option to simply continue.

The only exception may be that some implementations may provide debugger commands for interactively returning from individual stack frames; even then, however, such commands should never be used except by someone who has read the erring code and understands the consequences of continuing from that point. In particular, the programmer should feel confident about writing code like this:

    (defun wargames:no-win-scenario ()
      (when (true) (error "Pushing the button would be stupid."))
      (push-the-button))

In this scenario, there should be no chance that the function `error` will return and the button will be pushed.

Remark: It should be noted that the notion of ``no chance'' that the button will be pushed is relative only to the language model; it assumes that the language is accurately implemented. In practice, compilers have bugs, computers have glitches, and users have been known to interrupt at inopportune moments and use the debugger to return from arbitrary stack frames. Such violations of the language model are beyond the scope of the condition system but not necessarily beyond the scope of potential failures that the programmer should consider and defend against. The possibility of such unusual failures may of course also influence the design of code meant to handle less drastic situations, such as maintaining a database uncorrupted.-KMP and GLS

In some cases, the programmer may have a single, well-defined idea of a reasonable recovery strategy for this particular error. In that case, he can use the function `cerror`, which specifies information about what would happen if the user did simply continue from the call to `cerror`. For example:

    Lisp> (defun factorial (x)
            (cond ((not (typep x 'integer))
                   (error "~S is not a valid argument to FACTORIAL."
                          x))
                  ((minusp x)
                   (let ((x-magnitude (- x)))
                     (cerror "Compute -(~D!) instead."
                             "(-~D)! is not defined." x-magnitude)
                     (- (factorial x-magnitude))))
                  ((zerop x) 1)
                  (t (* x (factorial (- x 1))))))
     => FACTORIAL
    Lisp> (factorial -3)
    Error: (-3)! is not defined.
    To continue, type :CONTINUE followed by an option number:
     1: Compute -(3!) instead.
     2: Return to Lisp Toplevel.
    Debug> :continue 1
     => -6

Next: Trapping Errors Up: Survey of Concepts Previous: Survey of Concepts