Next: , Previous: Operating-System Interface, Up: Top

16 Error System

The MIT/GNU Scheme error system provides a uniform mechanism for the signalling of errors and other exceptional conditions. The simplest and most generally useful procedures in the error system are:

is used to signal simple errors, specifying a message and some irritant objects (see Condition Signalling). Errors are usually handled by stopping the computation and putting the user in an error repl.
is used to signal warnings (see Condition Signalling). Warnings are usually handled by printing a message on the console and continuing the computation normally.
is used to suppress the normal handling of errors within a given dynamic extent (see Condition Handling). Any error that occurs within the extent is trapped, returning immediately to the caller of ignore-errors.

More demanding applications require more powerful facilities. To give a concrete example, suppose you want floating-point division to return a very large number whenever the denominator is zero. This behavior can be implemented using the error system.

The Scheme arithmetic system can signal many different kinds of errors, including floating-point divide by zero. In our example, we would like to handle this particular condition specially, allowing the system to handle other arithmetic errors in its usual way.

The error system supports this kind of application by providing mechanisms for distinguishing different types of error conditions and for specifying where control should be transferred should a given condition arise. In this example, there is a specific object that represents the “floating-point divide by zero” condition type, and it is possible to dynamically specify an arbitrary Scheme procedure to be executed when a condition of that type is signalled. This procedure then finds the stack frame containing the call to the division operator, and returns the appropriate value from that frame.

Another useful kind of behavior is the ability to specify uniform handling for related classes of conditions. For example, it might be desirable, when opening a file for input, to gracefully handle a variety of different conditions associated with the file system. One such condition might be that the file does not exist, in which case the program will try some other action, perhaps opening a different file instead. Another related condition is that the file exists, but is read protected, so it cannot be opened for input. If these or any other related conditions occur, the program would like to skip this operation and move on to something else.

At the same time, errors unrelated to the file system should be treated in their usual way. For example, calling car on the argument 3 should signal an error. Or perhaps the name given for the file is syntactically incorrect, a condition that probably wants to be handled differently from the case of the file not existing.

To facilitate the handling of classes of conditions, the error system taxonomically organizes all condition types. The types are related to one another by taxonomical links, which specify that one type is a “kind of” another type. If two types are linked this way, one is considered to be a specialization of the other; or vice-versa, the second is a generalization of the first. In our example, all of the errors associated with opening an input file would be specializations of the condition type “cannot open input file”.

The taxonomy of condition types permits any condition type to have no more than one immediate generalization. Thus, the condition types form a forest (set of trees). While users can create new trees, the standard taxonomy (see Taxonomy) is rooted at condition-type:serious-condition, condition-type:warning, condition-type:simple-condition, and condition-type:breakpoint; users are encouraged to add new subtypes to these condition types rather than create new trees in the forest.

To summarize, the error system provides facilities for the following tasks. The sections that follow will describe these facilities in more detail.

Signalling a condition
A condition may be signalled in a number of different ways. Simple errors may be signalled, without explicitly defining a condition type, using error. The signal-condition procedure provides the most general signalling mechanism.
Handling a condition
The programmer can dynamically specify handlers for particular condition types or for classes of condition types, by means of the bind-condition-handler procedure. Individual handlers have complete control over the handling of a condition, and additionally may decide not to handle a particular condition, passing it on to previously bound handlers.
Restarting from a handler
The with-restart procedure provides a means for condition-signalling code to communicate to condition-handling code what must be done to proceed past the condition. Handlers can examine the restarts in effect when a condition was signalled, allowing a structured way to continue an interrupted computation.
Packaging condition state
Each condition is represented by an explicit object. Condition objects contain information about the nature of the condition, information that describes the state of the computation from which the condition arose, and information about the ways the computation can be restarted.
Classification of conditions
Each condition has a type, represented by a condition type object. Each condition type may be a specialization of some other condition types. A group of types that share a common generalization can be handled uniformly by specifying a handler for the generalization.