Previous: Multiple Restarts, Up: Common Lisp Conditions [Contents][Index]
While conditions are mainly used for error handling, they can be used for other purposes–you can use conditions, condition handlers, and restarts to build a variety of protocols between low- and high-level code. The key to understanding the potential of conditions is to understand that merely signaling a condition has no effect on the flow of control.
The primitive signaling function SIGNAL
implements the mechanism of searching
for an applicable condition handler and invoking its handler function. The
reason a handler can decline to handle a condition by returning normally is
because the call to the handler function is just a regular function call–when
the handler returns, control passes back to SIGNAL
, which then looks for
another, less recently bound handler that can handle the condition. If SIGNAL
runs out of condition handlers before the condition is handled, it also returns
normally.
The ERROR
function you’ve been using calls SIGNAL
. If the error is handled
by a condition handler that transfers control via HANDLER-CASE
or by invoking
a restart, then the call to SIGNAL
never returns. But if SIGNAL
returns,
ERROR
invokes the debugger by calling the function stored in
‘*DEBUGGER-HOOK*’. Thus, a call to ERROR
can never return normally; the
condition must be handled either by a condition handler or in the debugger.
Another condition signaling function, WARN
, provides an example of a
different kind of protocol built on the condition system. Like ERROR
, WARN
calls SIGNAL
to signal a condition. But if SIGNAL
returns, WARN
doesn’t
invoke the debugger–it prints the condition to ‘*ERROR-OUTPUT*’ and returns
‘NIL’, allowing its caller to proceed. WARN
also establishes a restart,
MUFFLE-WARNING
, around the call to SIGNAL
that can be used by a condition
handler to make WARN
return without printing anything. The restart function
MUFFLE-WARNING
finds and invokes its eponymous restart, signaling a
‘CONTROL-ERROR’ if no such restart is available. Of course, a condition
signaled with WARN
could also be handled in some other way–a condition
handler could "promote" a warning to an error by handling it as if it were an
error.
For instance, in the log-parsing application, if there were ways a log entry
could be slightly malformed but still parsable, you could write
parse-log-entry
to go ahead and parse the slightly defective entries but to
signal a condition with WARN
when it did. Then the larger application could
choose to let the warning print, to muffle the warning, or to treat the warning
like an error, recovering the same way it would from a
‘malformed-log-entry-error’.
A third error-signaling function, CERROR
, provides yet another protocol. Like
ERROR
, CERROR
will drop you into the debugger if the condition it signals
isn’t handled. But like WARN
, it establishes a restart before it signals the
condition. The restart, CONTINUE
, causes CERROR
to return normally–if the
restart is invoked by a condition handler, it will keep you out of the debugger
altogether. Otherwise, you can use the restart once you’re in the debugger to
resume the computation immediately after the call to CERROR
. The function
CONTINUE
finds and invokes the CONTINUE
restart if it’s available and
returns ‘NIL’ otherwise.
You can also build your own protocols on SIGNAL
–whenever low-level code
needs to communicate information back up the call stack to higher-level code,
the condition mechanism is a reasonable mechanism to use. But for most
purposes, one of the standard error or warning protocols should suffice.
Previous: Multiple Restarts, Up: Common Lisp Conditions [Contents][Index]