[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

error.proposal.9



This is probably the final version of my error proposal.  Unless there
are objections it is ready to convert to Scribe, render style-compatible
with the Common Lisp manual, and adopt.

Our experience is that error-handling of adequate flexibility and power
requires a complex error classification system, using flavors or the
equivalent.  Not everyone in the Common Lisp community agrees, and our
implementation of these ideas has only been in existence for half a
year, not long enough to consider it mature and stable.  Chapter 23 of
the laser manual is mainly a rehash of the old Lisp machine condition
system (documented in The Lisp Machine Manual), which was generally
found to be totally inadequate.  Therefore we propose that for the
present the language only standardize on ways to SIGNAL errors, not on
ways to HANDLE errors.  Of course a complete language requires
error-handling mechanisms, but many useful portable programs do not
require them.  Thus it makes sense to defer standardizing this until we
understand it better.

The rest of this proposal replaces everything in chapter 23 of the laser
manual.  Most uppercase text should be read as boldface.  Italics are
not indicated.

- Handling errors

When an error is signalled, either by calling one of the functions
documented in this section or directly by the Lisp system, it is handled in
an implementation-dependent way.  The general assumption is that each
implementation will provide an interactive debugger that prints the error
message, along with suitable contextual information such as what function
detected the error.  The user may interact with the debugger to examine or
modify the state of the program in various ways, including abandoning the
current computation ("aborting to top level") and continuing from the
error.  What "continuing" means depends on how the error is signalled;
details are given with each error signalling function.
[The Lisp machine calls continuing "proceeding".]


- General error signalling functions

ERROR format-string &rest args
Signal an error, with the message constructed by applying FORMAT to
the arguments.  It is impossible to continue from this kind of error;
thus the ERROR function will never return (barring someone meddling
with it by redefining it or by using an interactive debugger to force
it to return).

The error message should not contain a carriage return at the beginning
or end, and should not contain any sort of herald indicating that it is
an error.  The system will take care of these according to whatever its
preferred style may be.  Conventionally error messages are complete
English sentences, ending with a period.  Carriage returns in the middle
of long messages are okay.  There should be no indentation after a
carriage return in the middle of an error message.  If the debugger in
a particular implementation displays error messages indented (e.g. indented
by 7 spaces because they are prefixed by "Error: ") then it should take
care of inserting the appropriate amount of indentation into a multi-line
error message.  Similarly, a debugger that prefixes error messages with
semicolons should take care of inserting a semicolon at the beginning of
each line in a multi-line error message.  Note that even within a single
implementation, there may be more than one program that presents error
messages to the user, and they may use different styles, so the caller
of ERROR cannot provide for this in advance.

The debugger printout in the following example is not to be taken as a
Common Lisp requirement; it is only an example of what an implementation
might print when ERROR is called.
Example:
	(ERROR "The command ~S is unrecognized." CMD)
	Error: The command EMERGECNY-SHUTDOWN is unrecognized.
	Error signalled by function OPERATOR-COMMAND-EXECUTE.
	>
[The Lisp machine calls this FERROR].


CERROR error-format-string continue-format-string &rest args
Signal an error, with the message constructed by applying FORMAT to
error-format-string and args.  Continuing from such an error will cause
CERROR to return NIL.  Use CERROR rather than ERROR to signal errors for
which you have written recovery code.  The name stands for "continuable
error," which is too verbose to use for such a common function.

A message describing what will happen if the error is continued will be
constructed by applying FORMAT to continue-format-string and args.  This
message should be a one-line description of the effect of continuing.
It should be phrased as an English sentence in the imperative mood, just
like the documentation of a command or a function.  Typically it would
be used by an interactive debugger as the documentation of its
"continue" command.  The message supplied with CERROR should not include
any statement of how the "continue" command is given, since this may be
different for each debugger.  The debugger will insert this into the
message, according to its own particular user-interface style.

Examples:
	(UNLESS (= (LIST-LENGTH FORM) 3)
	  (CERROR "Wrong number of arguments in ~S"
		  (IF (< (LIST-LENGTH FORM) 3) "Assume 0 for missing args."
					       "Ignore extra args.")
		  FORM)
	  (SETQ FORM (APPEND FORM '(0 0))))
	Error: Wrong number of arguments in (F X)
	Error signalled by function EXAMPLE.
	If continued: Assume 0 for missing args.
	>

	(DO () ((KNOWN-WORDP X) X)
	  (CERROR "~S is unknown, probably misspelled."
		  "Replace ~S and try again." X)
	  (FORMAT T "~&New word: ")
	  (SETQ X (READ)))
	;This example could be done more briefly with ASSERT or
	;even with CHECK-TYPE (and SATISFIES).

In complex cases where the error message uses some of the args and the
continue message uses others of the args, it may be necessary to use the
~G or ~* operators of FORMAT to skip over unwanted arguments in one of the
FORMAT control strings.

[The Lisp machine calls this FSIGNAL, except that it returns :NO-ACTION
rather than NIL and it fails to distinguish between the error message
and the continue message.]


WARN format-string &rest args
Print an error message constructed by applying FORMAT to the arguments,
but don't go into the debugger.  WARN returns NIL.  This would just be
FORMAT with output directed to *ERROR-OUTPUT*, except that WARN takes
care of advancing to a fresh line before and after the error message and
may do other implementation-dependent things (for example, in Symbolics
Common Lisp, WARN messages printed during a compilation are associated
with the function being compiled and saved for later perusal by editor
tools.  Furthermore WARN automatically prints the name of the function
associated with the warning.)

[The Lisp machine calls this COMPILER:WARN, approximately.]


*BREAK-ON-WARNINGS*					Special Variable
If non-NIL, WARN prints its message and then goes to the debugger.  Continuing
causes WARN to return NIL.  If *BREAK-ON-WARNINGS* is NIL (the default),
then WARN just prints its message and returns NIL.  This flag is intended
for use when debugging programs that issue warnings and would not be turned
on in normal "production."


BREAK &optional format-string &rest args
Print the message and go directly into the debugger, without allowing
any possibility of interception by programmed error-handling facilities.
There aren't any error-handling facilities in Common Lisp, but there
might be in particular implementations, and there will be in Common Lisp
in the future.  When continued, BREAK returns NIL.  It is permissible to
call BREAK with no arguments; it will supply some default message.

BREAK is presumed to be used as a way of inserting temporary debugging
"breakpoints" in a program, not as a way of signalling errors.  Thus
continuing from a BREAK would never do anything special, and it does not
take the second FORMAT control-string argument that CERROR takes.  This
and the lack of any possibility of interception by programmed
error-handling are the only program-visible differences between BREAK
and CERROR.  The interactive debugger may choose to display them
differently, for instance a CERROR's message might be prefixed with
"Error:" and a BREAK's message prefixed with "Break:".  This depends on
the user-interface style of the particular implementation.

Compatibility note: Maclisp's BREAK takes two optional arguments.  The
first would be a string if Maclisp had strings.  The second is a boolean
value specifying whether BREAK should break or return immediately.  In
Common Lisp one makes a BREAK conditional by putting it inside a conditional
form such as WHEN or UNLESS.


- Specialized error-signalling special forms (or macros)

CHECK-TYPE place typespec [string]		Special Form
Signal an error if the contents of place is not of the desired type.
Continuing from this error will ask for a new value, store it into
place, and start over, checking the type of the new value and signalling
another error if it is still not of the desired type.  Subforms of
place may be evaluated multiple times, because of the implicit
loop generated.  CHECK-TYPE returns NIL.

place must be a generalized variable reference acceptable to SETF.
typespec must be a type expression; it is not evaluated.
string must be an English description of the type, starting with
an indefinite article (a or an); it is not evaluated.  If string is
not supplied, it is computed automatically from typespec.  The reason
that the optional string is allowed is that some usages of CHECK-TYPE
may prefer a more specific or more explanatory description of what is
wanted than can be generated automatically by the type system.

The error message will mention place, its contents, and the desired
type.  Some implementations may generate a somewhat differently worded
error message if they recognize that place is one of the arguments to
the function that called CHECK-TYPE.  Example:
	(SETQ X 'FOO)
	(CHECK-TYPE X (INTEGER 0 *) "a positive integer")
	Error: The value of X, FOO, is not a positive integer.
[The Lisp machine calls this CHECK-ARG-TYPE.]


ASSERT test-form [reference]* [string [arg]*]		Special Form
Signal an error if the value of test-form is false.  Continuing
from this error will allow the user to alter the values of some
variables and will then start over, evaluating the test-form again.
ASSERT returns NIL.

test-form is any form.  Each reference (there may be any number of them,
or none) is a generalized-variable reference acceptable to SETF.
These are variables that test-form depends on and that it makes sense to
allow the user to change when continuing from the error.  Subforms of
the references are only evaluated if an error is signalled, and may be
re-evaluated if the error is signalled again (after continuing without
actually fixing the problem).  string is an error message string, not
evaluated.  args are forms evaluated only if an error is signalled, and
re-evaluated if the error is signalled again.  FORMAT is applied to
string and args to get the error message.  If string is omitted, a
default error message such as "assertion failed" is used; in this case
the args must be omitted, since the string serves to delimit the
references from the args.

The test-form and references are not directly included in the error
message, but might be made available for the user's perusal by the
debugger.  If the user gives the continue command, he should be
presented with the opportunity to alter the values of any or all of the
references; the details of this depend on each particular
implementation's user-interface style, of course.

Examples:

	(ASSERT (VALVE-CLOSED-P V1))

	(ASSERT (VALVE-CLOSED-P V1) "Live steam is escaping!")

	(ASSERT (VALVE-CLOSED-P V1) (VALVE-MANUAL-CONTROL V1)
		"Live steam is escaping!")

	(ASSERT (<= MINBASE BASE MAXBASE) BASE
		"Base ~D is out of the range ~D-~D" BASE MINBASE MAXBASE)
	   ;Note that the user is invited to change BASE, but not the bounds.

	(ASSERT (= (ARRAY-DIMENSION A 1) (ARRAY-DIMENSION B 0)) A B
		"The matrix multiplication ~S x ~S cannot be performed" A B)


- Exhaustive case analysis special forms (or macros)

These macros are included in the standard language, even though a user
could write them himself using the other standard facilities provided,
for several reasons.  It is likely that many people will want these,
and so a standard consistent set should be provided rather than allowing
a variety of incompatible dialects to develop.  The E- versions are
just automatically-generated OTHERWISE clauses, but the C- versions
require some thought in order to implement correctly, so they should
be provided by the system so users don't have to puzzle them out on
their own.  Individual implementations may be able to do a better job
on these, using their own idiosyncratic facilities, than can be done
using the standard facilities.  Related to this is the fact that if the
implementation provides these, they will fit better into its particular
user-interface style.  There is also the argument from morality: most
people are too lazy to put in an otherwise clause to check for cases they
didn't anticipate, even if they would agree that they will probably get
shafted later.  By making this very easy to do (one character) we make
it more likely that people will take the trouble to do it.

ETYPECASE value [clauses]*				Special Form
The syntax is the same as TYPECASE, except that no OTHERWISE clause is
permitted.  If no clause is satisfied, ETYPECASE signals an error with
a message constructed from the clauses.  It is not permissible to
continue from this error.  To supply your own error message, use
TYPECASE with an OTHERWISE clause containing a call to ERROR.  The
name of this function stands for either "exhaustive type case" or
"error-checking type case".
Example:
	(SETQ X 1/3)
	(ETYPECASE X (INTEGER (- X)) (SYMBOL (INVERSE X)))
	Error: The value of X, 1/3, was neither an integer nor a symbol.


CTYPECASE reference [clauses]*				Special Form
The syntax is the same as TYPECASE, except that no OTHERWISE clause is
permitted.  The reference must be a generalized variable reference
acceptable to SETF.  If no clause is satisfied, CTYPECASE signals an
error with a message constructed from the clauses.  Continuing from this
error accepts a new value from the user, stores it into reference, and
starts over, making the type tests again.  Subforms of reference may be
evaluated multiple times.  The name of this function stands for
"continuable exhaustive type case".


ECASE value [clauses]*					Special Form
The syntax is the same as CASE, except that no OTHERWISE clause is
permitted.  If no clause is satisfied, ECASE signals an error with a
message constructed from the clauses.  It is not permissible to continue
from this error.  To supply your own error message, use CASE with an
OTHERWISE clause containing a call to ERROR.  The name of this function
stands for either "exhaustive case" or "error-checking case".
Example:
	(SETQ X 1/3)
	(ECASE X (ALPHA (FOO)) (OMEGA (BAR)))
	Error: The value of X, 1/3, is neither ALPHA nor OMEGA.


CCASE reference [clauses]*				Special Form
The syntax is the same as CASE, except that no OTHERWISE clause is
permitted.  The reference must be a generalized variable reference
acceptable to SETF.  If no clause is satisfied, CCASE signals an error
with a message constructed from the clauses.  Continuing from this error
accepts a new value from the user, stores it into reference, and starts
over, making the clause tests again.  Subforms of reference may be
evaluated multiple times.  The name of this function stands for
"continuable exhaustive case".


- Issues decided, perhaps by fiat    (last chance to change our minds!)

Error messages and continue messages are complete sentences starting
with a capital letter, thus you can't embed them in the middle of other
sentences.  No attempt is made to exploit the lack of conjugation of
verbs in English; this wouldn't work for other natural languages anyway.
Thus we don't say that a continue message could be printed by itself,
prefixed by "The continue command will", or prefixed by "Type continue
to" in different implementations.

*BREAK-ON-WARNINGS* is in.

The syntax of ASSERT depends on the use of the error message string as
a delimiter, rather than putting an extra level of parentheses around
the references.

We say (ECASE X (FOO ...) (BAR ...)) rather than
(CASE X (FOO ...) (BAR ...) (OTHERWISE-ERROR)) or
(CASE X (FOO ...) (BAR ...) OTHERWISE-ERROR).

Subforms of the references in CHECK-TYPE, ASSERT, CTYPECASE, and CCASE may
be evaluated multiple times, depending on what the implementation wants to do.


- Possible future extensions

We anticipate that some or all of the following will be added to Common
Lisp in the future, when they are better understood.  Some of these things
exist already as Symbolics extensions to Common Lisp and will be proposed
for standardization when things settle down a bit.  Should the manual
include this list, which may provide some rationale for what is in this
first version of the language?

A "condition system" providing names for unusual conditions (including
but not limited to errors) and a taxonomic system for classifying those
names.

Extension of FERROR, CERROR, and WARN so that if the first argument
is a symbol, it is taken to be a condition name.  In this case the
format-string is the second argument.  If no condition name is specified,
a default condition name with no interesting properties is assumed.

Ways to define the behavior and relationships of conditions.  Ways to
use conditions as inter-module interfaces.  Ways to use conditions to
customize the behavior of the interactive debugger.

Ways to establish condition handlers, so that programs may respond to
conditions, by throwing out, by continuing, or by correcting the reason
for the condition and retrying.

Formalization of the notion of "aborting a program," and provision of
mechanisms to define where control will be thrown to when the program is
aborted, as well as a function to abort the program.  This is more
complex than it seems because of interaction with the interactive
debugger and the condition system.

A way to trap errors without going into the debugger, within a certain
dynamic scope.

Portable debugger details, e.g. TRACE and BREAKON commands.

Facilities making it possible for a user to write a portable debugger.

Portable floating-point exception, overflow, and rounding control.

A way to define the default description string for a user-defined type,
used when CHECK-TYPE is called with only two subforms.  This can of
course be a function of the type's parameters when the type is not
simply a symbol.