[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16. Error System

The MIT 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:

error
is used to signal simple errors, specifying a message and some irritant objects (see section 16.1 Condition Signalling). Errors are usually handled by stopping the computation and putting the user in an error REPL.

warn
is used to signal warnings (see section 16.1 Condition Signalling). Warnings are usually handled by printing a message on the console and continuing the computation normally.

ignore-errors
is used to suppress the normal handling of errors within a given dynamic extent (see section 16.3 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 section 16.7 Condition-Type 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.

16.1 Condition Signalling  
16.2 Error Messages  
16.3 Condition Handling  
16.4 Restarts  
16.5 Condition Instances  
16.6 Condition Types  
16.7 Condition-Type Taxonomy  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.1 Condition Signalling

Once a condition instance has been created using make-condition (or any condition constructor), it can be signalled. The act of signalling a condition is separated from the act of creating the condition to allow more flexibility in how conditions are handled. For example, a condition instance could be returned as the value of a procedure, indicating that something unusual has happened, to allow the caller to clean up some state. The caller could then signal the condition once it is ready.

A more important reason for having a separate condition-signalling mechanism is that it allows resignalling. When a signalled condition has been caught by a particular handler, and the handler decides that it doesn't want to process that particular condition, it can signal the condition again. This is one way to allow other handlers to get a chance to see the condition.

procedure+: error reason argument...
This is the simplest and most common way to signal a condition that requires intervention before a computation can proceed (when intervention is not required, warn is more appropriate). error signals a condition (using signal-condition), and if no handler for that condition alters the flow of control (by invoking a restart, for example) it calls the procedure standard-error-handler, which normally prints an error message and stops the computation, entering an error REPL. Under normal circumstances error will not return a value (although an interactive debugger can be used to force this to occur).

Precisely what condition is signalled depends on the first argument to error. If reason is a condition, then that condition is signalled and the arguments are ignored. If reason is a condition type, then a new instance of this type is generated and signalled; the arguments are used to generate the values of the fields for this condition type (they are passed as the field-plist argument to make-condition). In the most common case, however, reason is neither a condition nor a condition type, but rather a string or symbol. In this case a condition of type condition-type:simple-error is created with the message field containing the reason and the irritants field containing the arguments.

procedure+: warn reason argument...
When a condition is not severe enough to warrant intervention, it is appropriate to signal the condition with warn rather than error. As with error, warn first calls signal-condition; the condition that is signalled is chosen exactly as in error except that a condition of type condition-type:simple-warning is signalled if reason is neither a condition nor a condition type. If the condition is not handled, warn calls the procedure standard-warning-handler, which normally prints a warning message and continues the computation by returning from warn.

warn establishes a restart named muffle-warning before calling signal-condition. This allows a signal handler to prevent the generation of the warning message by calling muffle-warning. The value of a call to warn is unspecified.

procedure+: signal-condition condition
This is the fundamental operation for signalling a condition. The precise operation of signal-condition depends on the condition type of which condition is an instance, the condition types set by break-on-signals, and the handlers established by bind-condition-handler and bind-default-condition-handler.

If the condition is an instance of a type that is a specialization of any of the types specified by break-on-signals, then a breakpoint REPL is initiated. Otherwise (or when that REPL returns), the handlers established by bind-condition-handler are checked, most recent first. Each applicable handler is invoked, and the search for a handler continues if the handler returns normally. If all applicable handlers return, then the applicable handlers established by bind-default-condition-handler are checked, again most recent first. Finally, if no handlers apply (or all return in a normal manner), signal-condition returns an unspecified value.

Note: unlike many other systems, the MIT Scheme runtime library does not establish handlers of any kind. (However, the Edwin text editor uses condition handlers extensively.) Thus, calls to signal-condition will return to the caller unless there are user supplied condition handlers, as the following example shows:

 
(signal-condition
 (make-condition
  condition-type:error
  (call-with-current-continuation (lambda (x) x))
  '()    ; no restarts
  '()))  ; no fields
=>  unspecified


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.2 Error Messages

By convention, error messages (and in general, the reports generated by write-condition-report) should consist of one or more complete sentences. The usual rules for sentences should be followed: the first word of the sentence should be capitalized, and the sentence should be terminated by a period. The message should not contain extraneous whitespace such as line breaks or indentation.

The error system provides a simple formatting language that allows the programmer to have some control over the printing of error messages. This formatting language will probably be redesigned in a future release.

Error messages typically consist of a string describing the error, followed by some irritant objects. The string is printed using display, and the irritants are printed using write, typically with a space between each irritant. To allow simple formatting, we introduce a noise object, printed using display. The irritant list may contain ordinary objects interspersed with noise objects. Each noise object is printed using display, with no extra whitespace, while each normal object is printed using write, prefixed by a single space character.

Here is an example:

 
(define (error-within-procedure message irritant procedure)
  (error message
         irritant
         (error-irritant/noise "within procedure")    
         procedure      
         (error-irritant/noise ".")))

This would format as follows:

 
(error-within-procedure "Bad widget" 'widget-32 'invert-widget) error-->

Bad widget widget-32 within procedure invert-widget.

Here are the operations supporting error messages:

procedure+: format-error-message message irritants port
Message is typically a string (although this is not required), irritants a list of irritant objects, and port an output port. Formats message and irritants to port in the standard way. Note that, during the formatting process, the depth and breadth to which lists are printed are each limited to small numbers, to guarantee that the output from each irritant is not arbitrarily large.

procedure+: error-irritant/noise value
Creates and returns a noise object whose value is value.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.3 Condition Handling

The occurrence of a condition is signalled using signal-condition. signal-condition attempts to locate and invoke a condition handler that is prepared to deal with the type of condition that has occurred. A condition handler is a procedure of one parameter, the condition that is being signalled. A procedure is installed as a condition handler by calling bind-condition-handler (to establish a handler that is in effect only while a particular thunk is executing) or bind-default-condition-handler (to establish a handler that is in effect permanently). As implied by the name, handlers created by bind-default-condition-handler are invoked only after all other applicable handlers have been invoked.

A handler may process a signal in any way it deems appropriate, but the common patterns are:

Ignore the condition.
By returning from the handler in the usual manner.

Handle the condition.
By doing some processing and then invoking a restart (or, less preferably, a continuation) that was established at some point prior to the call to signal-condition.

Resignal a condition.
By doing some processing and calling signal-condition with either the same condition or a newly created one. In order to support this, signal-condition runs handler in such a way that a subsequent call to signal-condition sees only the handlers that were established prior to this one.

As an aid to debugging condition handlers, Scheme maintains a set of condition types that will cause an interactive breakpoint to occur prior to normal condition signalling. That is, signal-condition creates a new REPL prior to its normal operation when its argument is a condition that is a specialization of any of these types. The procedure break-on-signals establishes this set of condition types.

procedure+: ignore-errors thunk
Executes thunk with a condition handler that intercepts the signalling of any specialization of condition-type:error (including those produced by calls to error) and immediately terminates the execution of thunk and returns from the call to ignore-errors with the signalled condition as its value. If thunk returns normally, its value is returned from ignore-errors.

Notice that ignore-errors does not "turn off signalling" or condition handling. Condition handling takes place in the normal manner but conditions specialized from condition-type:error are trapped rather than propogated as they would be by default.

procedure+: bind-condition-handler condition-types handler thunk
Invokes thunk after adding handler as a condition handler for the conditions specified by condition-types. Condition-types must be a list of condition types; signalling a condition whose type is a specialization of any of these types will cause the handler to be invoked. See signal-condition for a description of the mechanism used to invoke handlers.

By special extension, if condition-types is the empty list then the handler is called for all conditions.

procedure+: bind-default-condition-handler condition-types handler
Installs handler as a (permanent) condition handler for the conditions specified by condition-types. Condition-types must be a list of condition types; signalling a condition whose type is a specialization of any of these types will cause the handler to be invoked. See signal-condition for a description of the mechanism used to invoke handlers.

By special extension, if condition-types is the empty list then the handler is called for all conditions.

procedure+: break-on-signals condition-types
Arranges for signal-condition to create an interactive REPL before it signals a condition that is a specialization of any of the types in the list of condition-types. This can be extremely helpful when trying to debug code that uses custom condition handlers. In order to create a REPL when any condition type is signalled it is best to actually put a breakpoint on entry to signal-condition.

procedure+: standard-error-handler condition
Called internally by error after it calls signal-condition. Normally creates creates a new REPL with the prompt "error>" (but see standard-error-hook). In order to simulate the effect of calling error, code may call signal-condition directly and then call standard-error-handler if signal-condition returns.

variable+: standard-error-hook
This variable controls the behavior of the procedure standard-error-handler, and hence error. It is intended to be bound with fluid-let and is normally #f. It may be changed to a procedure of one argument and will then be invoked (with standard-error-hook rebound to #f) by standard-error-handler just prior to starting the error REPL. It is passed one argument, the condition being signalled.

procedure+: standard-warning-handler condition
This is the procedure called internally by warn after it calls signal-condition. The normal behavior of standard-warning-handler is to print a message (but see standard-warning-hook). More precisely, the message is printed to the port returned by notification-output-port. The message is formed by first printing the string "Warning: " to this port, and then calling write-condition-report on condition and the port.

In order to simulate the effect of calling warn, code may call signal-condition directly and then call standard-warning-handler if signal-condition returns. (This is not sufficient to implement the muffle-warning protocol, however. For that purpose an explicit restart must be provided.)

variable+: standard-warning-hook
This variable controls the behavior of the procedure standard-warning-handler, and hence warn. It is intended to be bound with fluid-let and is normally #f. It may be changed to a procedure of one argument and will then be invoked (with standard-warning-hook rebound to #f) by standard-warning-handler in lieu of writing the warning message. It is passed one argument, the condition being signalled.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.4 Restarts

The Scheme error system provides a mechanism, known as restarts, that helps coordinate condition-signalling code with condition-handling code. A module of code that detects and signals conditions can provide procedures (using with-simple-restart or with-restart) to be invoked by handlers that wish to continue, abort, or restart the computation. These procedures, called restart effectors, are encapsulated in restart objects.

When a condition object is created, it contains a set of restart objects, each of which contains a restart effector. Condition handlers can inspect the condition they are handling (using find-restart to find restarts by name, or condition/restarts to see the entire set), and they can invoke the associated effectors (using invoke-restart or invoke-restart-interactively). Effectors can take arguments, and these may be computed directly by the condition-handling code or by gathering them interactively from the user.

The names of restarts can be chosen arbitrarily, but the choice of name is significant. These names are used to coordinate between the signalling code (which supplies names for restarts) and the handling code (which typically chooses a restart effector by the name of its restart). Thus, the names specify the restart protocol implemented by the signalling code and invoked by the handling code. The protocol indicates the number of arguments required by the effector code as well as the semantics of the arguments.

Scheme provides a conventional set of names (hence, protocols) for common use. By choosing the names of restarts from this set, signalling code can indicate that it is able to perform a small set of fairly common actions (abort, continue, muffle-warning, retry, store-value, use-value). In turn, simple condition-handling code can look for the kind of action it wishes to perform and simply invoke it by name. All of Scheme's conventional names are symbols, although in general restart names are not restricted to any particular data type. In addition, the object #f is reserved to indicate the "not for automated use" protocol: these restarts should be activated only under human control.

Restarts themselves are first-class objects. They encapsulate their name, a procedure (known as the effector) to be executed if they are invoked, and a thunk (known as the reporter) that can be invoked to display a description of the restart (used, for example, by the interactive debugger). Invoking a restart is an indication that a handler has chosen to accept control for a condition; as a consequence, the effector of the restart should not return, since this would indicate that the handler declined to handle the condition. Thus, the effector should call a continuation captured before the condition-signalling process began. The most common pattern of usage by signalling code is encapsulated in with-simple-restart.

Within this chapter, a parameter named restarts will accept any of the following values:

16.4.1 Establishing Restart Code  
16.4.2 Invoking Standard Restart Code  
16.4.3 Finding and Invoking General Restart Code  
16.4.4 The Named Restart Abstraction  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.4.1 Establishing Restart Code

procedure+: with-simple-restart name reporter thunk
Invokes thunk in a dynamic environment created by adding a restart named name to the existing named restarts. Reporter may be used during the execution of thunk to produce a description of the newly created restart; it must either be a procedure of one argument (a port) or a string. By convention, the description generated by reporter should be a short complete sentence, with first word capitalized and terminated by a period. The sentence should fit on one line with a little room to spare (see the examples below); usually this means that the sentence should be 70 characters or less in length.

If the restart created by with-simple-restart is invoked it simply aborts the computation in progress by returning an unspecified value from the call to with-simple-restart. Otherwise with-simple-restart returns the value computed by thunk.

 
(with-simple-restart 'george "This restart is named george."
  (lambda () 3)) => 3
  
(with-simple-restart 'george "This restart is named george."
  (lambda ()
    (invoke-restart (find-restart 'george)))) => unspecific

(with-simple-restart 'george "This restart is named george."
  (lambda () (car 3)))
;The object 3, passed as the first argument to car,
; is not the correct type.
;To continue, call RESTART with an option number:
; (RESTART 3) => Specify an argument to use in its place.
; (RESTART 2) => This restart is named george.
; (RESTART 1) => Return to read-eval-print level 1.

procedure+: with-restart name reporter effector interactor thunk
Invokes thunk in a dynamic environment created by adding a restart named name to the existing named restarts. Reporter may be used during the execution of thunk to produce a description of the newly created restart; it must either be a procedure of one argument (a port) or a string. Effector is a procedure which will be called when the restart is invoked by invoke-restart. Interactor specifies the arguments that are to be passed to effector when it is invoked interactively; it may be either a procedure of no arguments, or #f. If interactor is #f, this restart is not meant to be invoked interactively.

The value returned by with-restart is the value returned by thunk. Should the restart be invoked by a condition handler, however, the effector will not return back to the handler that invoked it. Instead, the effector should call a continuation created before the condition-signalling process began, and with-restart will therefore not return in the normal manner.

 
(define (by-george! thunk)
  ; This code handles conditions that arise while executing thunk
  ; by invoking the GEORGE restart, passing 1 and 2 to the restart's
  ; effector code.
  (bind-condition-handler '() ; All conditions
   (lambda (condition)
     (invoke-restart (find-restart 'george) 1 2))
   thunk))

(define (can-george! thunk)
  ; This code provides a way of handling errors: the GEORGE restart.
  ; In order to GEORGE you must supply two values.
  (lambda ()
    (call-with-current-continuation
     (lambda (kappa)
       (with-restart
        'george                         ; Name
        "This restart is named george." ; Reporter
        (lambda (a b)                   ; Effector
          (kappa (list 'george a b)))
        values                          ; Interactor
        thunk)))))                      ; Thunk

(by-george! (can-george! (lambda () -3))        => -3
(by-george! (can-george! (lambda () (car 'x)))) => (george 1 2)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.4.2 Invoking Standard Restart Code

Scheme supports six standard protocols for restarting from a condition, each encapsulated using a named restart (for use by condition-signalling code) and a simple procedure (for use by condition-handling code). Unless otherwise specified, if one of these procedures is unable to find its corresponding restart, it returns immediately with an unspecified value.

Each of these procedures accepts an optional argument restarts, which is described above in 16.4 Restarts.

procedure+: abort [restarts]
Abort the computation, using the restart named abort. The corresponding effector takes no arguments and abandons the current line of computation. This is the restart provided by Scheme's REPL.

If there is no restart named abort, this procedure signals an error of type condition-type:no-such-restart.

procedure+: continue [restarts]
Continue the current computation, using the restart named continue. The corresponding effector takes no arguments and continues the computation beyond the point at which the condition was signalled.

procedure+: muffle-warning [restarts]
Continue the current computation, using the restart named muffle-warning. The corresponding effector takes no arguments and continues the computation beyond the point at which any warning message resulting from the condition would be presented to the user. The procedure warn establishes a muffle-warning restart for this purpose.

If there is no restart named muffle-warning, this procedure signals an error of type condition-type:no-such-restart.

procedure+: retry [restarts]
Retry the current computation, using the restart named retry. The corresponding effector takes no arguments and simply retries the same computation that triggered the condition. The condition may reoccur, of course, if the root cause has not been eliminated. The code that signals a "file does not exist" error can be expected to supply a retry restart. The restart would be invoked after first creating the missing file, since the computation is then likely to succeed if it is simply retried.

procedure+: store-value new-value [restarts]
Retry the current computation, using the restart named store-value, after first storing new-value. The corresponding effector takes one argument, new-value, and stores it away in a restart-dependent location, then retries the same computation that triggered the condition. The condition may reoccur, of course, if the root cause has not been eliminated. The code that signals an "unassigned variable" error can be expected to supply a store-value restart; this would store the value in the variable and continue the computation.

procedure+: use-value new-value [restarts]
Retry the current computation, using the restart named use-value, but substituting new-value for a value that previously caused a failure. The corresponding effector takes one argument, new-value, and retries the same computation that triggered the condition with the new value substituted for the failing value. The condition may reoccur, of course, if the new value also induces the condition.

The code that signals an "unassigned variable" error can be expected to supply a use-value restart; this would simply continue the computation with new-value instead of the value of the variable. Contrast this with the retry and store-value restarts. If the retry restart is used it will fail because the variable still has no value. The store-value restart could be used, but it would alter the value of the variable, so that future references to the variable would not be detected.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.4.3 Finding and Invoking General Restart Code

Restarts are a general mechanism for establishing a protocol between condition-signalling and condition-handling code. The Scheme error system provides "packaging" for a number of common protocols. It also provides lower-level hooks that are intended for implementing customized protocols. The mechanism used by signalling code (with-restart and with-simple-restart) is used for both purposes.

Four additional operations are provided for the use of condition-handling code. Two operations (bound-restarts and find-restart) allow condition-handling code to locate active restarts. The other two operations (invoke-restart and invoke-restart-interactively) allow restart effectors to be invoked once the restart object has been located.

In addition, there is a data abstraction that provides access to the information encapsulated in restart objects.

procedure+: bound-restarts
Returns a list of all currently active restart objects, most recently installed first. bound-restarts should be used with caution by condition-handling code, since it reveals all restarts that are active at the time it is called, rather than at the time the condition was signalled. It is useful, however, for collecting the list of restarts for inclusion in newly generated condition objects or for inspecting the current state of the system.

procedure+: find-restart name [restarts]
Returns the first restart object named name in the list of restarts (permissible values for restarts are described above in 16.4 Restarts). When used in a condition handler, find-restart is usually passed the name of a particular restart and the condition object that has been signalled. In this way the handler finds only restarts that were available when the condition was created (usually the same as when it was signalled). If restarts is omitted, the currently active restarts would be used, and these often include restarts added after the condition ocurred.

procedure+: invoke-restart restart argument...
Calls the restart effector encapsulated in restart, passing the specified arguments to it. invoke-restart is intended for use by condition-handling code that understands the protocol implemented by restart, and can therefore calculate and pass an appropriate set of arguments.

If a condition handler needs to interact with a user to gather the arguments for an effector (e.g. if it does not understand the protocol implemented by restart) invoke-restart-interactively should be used instead of invoke-restart.

procedure+: invoke-restart-interactively restart
First calls the interactor encapsulated in restart to interactively gather the arguments needed for restart's effector. It then calls the effector, passing these arguments to it.

invoke-restart-interactively is intended for calling interactive restarts (those for which restart/interactor is not #f). For convenience, invoke-restart-interactively will call the restart's effector with no arguments if the restart has no interactor; this behavior may change in the future.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.4.4 The Named Restart Abstraction

A restart object is very simple, since it encapsulates only a name, effector, interactor, and description.

procedure+: restart? object
Returns #f if and only if object is not a restart.

procedure+: restart/name restart
Returns the name of restart. While the Scheme error system uses only symbols and the object #f for its predefined names, programs may use arbitrary objects (name equivalence is tested using eq?).

procedure+: restart/effector restart
Returns the effector encapsulated in restart. Normally this procedure is not used since invoke-restart and invoke-restart-interactively capture the most common invocation patterns.

procedure+: restart/interactor restart
Returns the interactor encapsulated in restart. This is either a procedure of no arguments or the object #f. Normally this procedure is not used since invoke-restart-interactively captures the most common usage. Thus restart/interactor is most useful as a predicate to determine if restart is intended to be invoked interactively.

procedure+: write-restart-report restart port
Writes a description of restart to port. This works by either displaying (if it is a string) or calling (if it is a procedure) the reporter that was supplied when the restart was created.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.5 Condition Instances

A condition, in addition to the information associated with its type, usually contains other information that is not shared with other conditions of the same type. For example, the condition type associated with "unbound variable" errors does not specify the name of the variable that was unbound. The additional information is captured in a condition object, also called a condition instance.

In addition to information that is specific to a given type of condition (such as the variable name for "unbound variable" conditions), every condition instance also contains a continuation that encapsulates the state of the computation in which the condition occurred. This continuation is used for analyzing the computation to learn more about the context in which the condition occurred. It is not intended to provide a mechanism for continuing the computation; that mechanism is provided by restarts.

16.5.1 Generating Operations on Conditions  
16.5.2 Condition Abstraction  
16.5.3 Simple Operations on Condition Instances  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.5.1 Generating Operations on Conditions

Scheme provides four procedures that take a condition type as input and produce operations on the corresponding condition object. These are reminiscent of the operations on record types that produce record operators (see section 10.4 Records). Given a condition type it is possible to generate: a constructor for instances of the type (using condition-constructor); an accessor to extract the contents of a field in instances of the type (using condition-accessor); a predicate to test for instances of the type (using condition-predicate); and a procedure to create and signal an instance of the type (using condition-signaller).

Notice that the creation of a condition object is distinct from signalling an occurrence of the condition. Condition objects are first-class; they may be created and never signalled, or they may be signalled more than once. Further notice that there are no procedures for modifying conditions; once created, a condition cannot be altered.

procedure+: condition-constructor condition-type field-names
Returns a constructor procedure that takes as arguments values for the fields specified in field-names and creates a condition of type condition-type. Field-names must be a list of symbols that is a subset of the field-names in condition-type. The constructor procedure returned by condition-constructor has signature

 
(lambda (continuation restarts . field-values) ...)

where the field-names correspond to the field-values. The constructor argument restarts is described in 16.4 Restarts. Conditions created by the constructor procedure have #f for the values of all fields other than those specified by field-names.

For example, the following procedure make-simple-warning constructs a condition of type condition-type:simple-warning given a continuation (where the condition occurred), a description of the restarts to be made available, a warning message, and a list of irritants that caused the warning:

 
(define make-simple-warning
  (condition-constructor condition-type:simple-warning
                         '(message irritants)))

procedure+: condition-accessor condition-type field-name
Returns a procedure that takes as input a condition object of type condition-type and extracts the contents of the specified field-name. condition-accessor signals error:bad-range-argument if the field-name isn't one of the named fields of condition-type; the returned procedure will signal error:wrong-type-argument if passed an object other than a condition of type condition-type or one of its specializations.

If it is known in advance that a particular field of a condition will be accessed repeatedly it is worth constructing an accessor for the field using condition-accessor rather than using the (possibly more convenient, but slower) access-condition procedure.

procedure+: condition-predicate condition-type
Returns a predicate procedure for testing whether an object is a condition of type condition-type or one of its specializations (there is no predefined way to test for a condition of a given type but not a specialization of that type).

procedure+: condition-signaller condition-type field-names default-handler
Returns a signalling procedure with parameters field-names. When the signalling procedure is called it creates and signals a condition of type condition-type. If the condition isn't handled (i.e. if no handler is invoked that causes an escape from the current continuation) the signalling procedure reduces to a call to default-handler with the condition as its argument.

There are several standard procedures that are conventionally used for default-handler. If condition-type is a specialization of condition-type:error, default-handler should be the procedure
standard-error-handler. If condition-type is a specialization of condition-type:warning, default-handler should be the procedure standard-warning-handler. If condition-type is a specialization of condition-type:breakpoint, default-handler should be the procedure standard-breakpoint-handler.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.5.2 Condition Abstraction

The condition data type is abstracted through a predicate condition? and a set of accessor procedures.

procedure+: condition? object
Returns #f if and only if object is not a condition.

procedure+: condition/type condition
Returns the condition type of which condition is an instance.

procedure+: condition/error? condition
Returns #t if the condition is an instance of condition type condition-type:error or a specialization of it, #f otherwise.

procedure+: condition/restarts condition
Returns the list of restarts specified when condition was created.

procedure+: condition/continuation condition
Returns the continuation specified when condition was created. This is provided for inspecting the state of the system when the condition occurred, not for continuing or restarting the computation.

procedure+: write-condition-report condition port
Writes a description of condition to port, using the reporter function from the condition type associated with condition. See also condition/report-string.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.5.3 Simple Operations on Condition Instances

The simple procedures described in this section are built on top of the more detailed abstraction of condition objects described above. While these procedures are sometimes easier to use, they are often less efficient.

procedure+: make-condition condition-type continuation restarts field-plist
Create a new condition object as an instance of condition-type, associated with continuation. The continuation is provided for inspection purposes only, not for restarting the computation. The restarts argument is described in 16.4 Restarts. The field-plist is an alternating list of field names and values for those fields, where the field names are those that would be returned by (condition-type/field-names condition-type). It is used to provide values for fields in the condition object; fields with no value specified are set to #f. Once a condition object has been created there is no way to alter the values of these fields.

procedure+: access-condition condition field-name
Returns the value stored in the field field-name within condition. Field-name must be one of the names returned by (condition-type/field-names (condition/type condition)). access-condition looks up the field-name at runtime, so it is more efficient to use condition-accessor to create an access function if the same field is to be extracted from several instances of the same condition type.

procedure+: condition/report-string condition
Returns a string containing a report of the condition. This is generated by calling write-condition-report on condition and a string output port, and returning the output collected by the port as a string.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.6 Condition Types

Each condition has a condition type object associated with it. These objects are used as a means of focusing on related classes of conditions, first by concentrating all of the information about a specific class of condition in a single place, and second by specifying an inheritance relationship between types. This inheritance relationship forms the taxonomic structure of the condition hierarchy (see section 16.7 Condition-Type Taxonomy).

The following procedures consititute the abstraction for condition types.

procedure+: make-condition-type name generalization field-names reporter
Creates and returns a (new) condition type that is a specialization of generalization (if it is a condition type) or is the root of a new tree of condition types (if generalization is #f). For debugging purposes, the condition type has a name, and instances of this type contain storage for the fields specified by field-names (a list of symbols) in addition to the fields common to all conditions (type, continuation and restarts).

Reporter is used to produce a description of a particular condition of this type. It may be a string describing the condition, a procedure of arity two (the first argument will be a condition of this type and the second a port) that will write the message to the given port, or #f to specify that the reporter should be taken from the condition type generalization (or produce an "undocumented condition of type ..." message if generalization is #f). The conventions used to form descriptions are spelled out in 16.2 Error Messages.

procedure+: condition-type/error? condition-type
Returns #t if the condition-type is condition-type:error or a specialization of it, #f otherwise.

procedure+: condition-type/field-names condition-type
Returns a list of all of the field names for a condition of type condition-type. This is the set union of the fields specified when this condition-type was created with the condition-type/field-names of the generalization of this condition-type.

procedure+: condition-type/generalizations condition-type
Returns a list of all of the generalizations of condition-type. Notice that every condition type is considered a generalization of itself.

procedure+: condition-type? object
Returns #f if and only if object is not a condition type.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

16.7 Condition-Type Taxonomy

The MIT Scheme error system provides a rich set of predefined condition types. These are organized into a forest through taxonomic links providing the relationships for "specializes" and "generalizes". The chart appearing below shows these relationships by indenting all the specializations of a given type relative to the type. Note that the variables that are bound to these condition types are prefixed by `condition-type:'; for example, the type appearing in the following table as `simple-error' is stored in the variable condition-type:simple-error. Users are encouraged to add new condition types by creating specializations of existing ones.

Following the chart are detailed descriptions of the predefined condition types. Some of these types are marked as abstract types. Abstract types are not intended to be used directly as the type of a condition; they are to be used as generalizations of other types, and for binding condition handlers. Types that are not marked as abstract are concrete; they are intended to be explicitly used as a condition's type.

 
serious-condition 
    error 
        simple-error
        illegal-datum
            wrong-type-datum
                wrong-type-argument
                wrong-number-of-arguments
            datum-out-of-range 
                bad-range-argument
            inapplicable-object
        file-error
            file-operation-error
            derived-file-error
        port-error
            derived-port-error
        variable-error
            unbound-variable
            unassigned-variable
        arithmetic-error
            divide-by-zero
            floating-point-overflow
            floating-point-underflow
        control-error
            no-such-restart
        not-loading 
        primitive-procedure-error
            system-call-error
warning
    simple-warning
simple-condition
breakpoint

condition type+: condition-type:serious-condition
This is an abstract type. All serious conditions that require some form of intervention should inherit from this type. In particular, all errors inherit from this type.

condition type+: condition-type:error
This is an abstract type. All errors should inherit from this type.

condition type+: condition-type:simple-error message irritants
This is the condition generated by the error procedure when its first argument is not a condition or condition type. The fields message and irritants are taken directly from the arguments to error; message contains an object (usually a string) and irritants contains a list of objects. The reporter for this type uses format-error-message to generate its output from message and irritants.

condition type+: condition-type:illegal-datum datum
This is an abstract type. This type indicates the class of errors in which a program discovers an object that lacks specific required properties. Most commonly, the object is of the wrong type or is outside a specific range. The datum field contains the offending object.

condition type+: condition-type:wrong-type-datum datum type
This type indicates the class of errors in which a program discovers an object that is of the wrong type. The type field contains a string describing the type that was expected, and the datum field contains the object that is of the wrong type.

 
(error:wrong-type-datum 3.4 "integer")  error-->
;The object 3.4 is not an integer.
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.

procedure+: error:wrong-type-datum datum type
This procedure signals a condition of type condition-type:wrong-type-datum. The datum and type fields of the condition are filled in from the corresponding arguments to the procedure.

condition type+: condition-type:wrong-type-argument datum type operator operand
This type indicates that a procedure was passed an argument of the wrong type. The operator field contains the procedure (or a symbol naming the procedure), the operand field indicates the argument position that was involved (this field contains either a symbol, a non-negative integer, or #f), the type field contains a string describing the type that was expected, and the datum field contains the offending argument.

 
(+ 'a 3)                                error-->
;The object a, passed as the first argument to integer-add,
; is not the correct type.
;To continue, call RESTART with an option number:
; (RESTART 2) => Specify an argument to use in its place.
; (RESTART 1) => Return to read-eval-print level 1.

(list-copy 3)
;The object 3, passed as an argument to list-copy, is not a list.
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.

procedure+: error:wrong-type-argument datum type operator
This procedure signals a condition of type condition-type:wrong-type-argument. The datum, type and operator fields of the condition are filled in from the corresponding arguments to the procedure; the operand field of the condition is set to #f.

condition type+: condition-type:wrong-number-of-arguments datum type operands
This type indicates that a procedure was called with the wrong number of arguments. The datum field contains the procedure being called, the type field contains the number of arguments that the procedure accepts, and the operands field contains a list of the arguments that were passed to the procedure.

 
(car 3 4)                               error-->
;The procedure car has been called with 2 arguments;
; it requires exactly 1 argument.
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.

procedure+: error:wrong-number-of-arguments datum type operands
This procedure signals a condition of type condition-type:wrong-number-of-arguments. The datum, type and operands fields of the condition are filled in from the corresponding arguments to the procedure.

condition type+: condition-type:datum-out-of-range datum
This type indicates the class of errors in which a program discovers an object that is of the correct type but is otherwise out of range. Most often, this type indicates that an index to some data structure is outside of the range of indices for that structure. The datum field contains the offending object.

 
(error:datum-out-of-range 3)            error-->
;The object 3 is not in the correct range.
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.

procedure+: error:datum-out-of-range datum
This procedure signals a condition of type condition-type:datum-out-of-range. The datum field of the condition is filled in from the corresponding argument to the procedure.

condition type+: condition-type:bad-range-argument datum operator operand
This type indicates that a procedure was passed an argument that is of the correct type but is otherwise out of range. Most often, this type indicates that an index to some data structure is outside of the range of indices for that structure. The operator field contains the procedure (or a symbol naming the procedure), the operand field indicates the argument position that was involved (this field contains either a symbol, a non-negative integer, or #f), and the datum field is the offending argument.

 
(string-ref "abc" 3)                    error-->
;The object 3, passed as the second argument to string-ref,
; is not in the correct range.
;To continue, call RESTART with an option number:
; (RESTART 2) => Specify an argument to use in its place.
; (RESTART 1) => Return to read-eval-print level 1.

procedure+: error:bad-range-argument datum operator
This procedure signals a condition of type condition-type:bad-range-argument. The datum and operator fields of the condition are filled in from the corresponding arguments to the procedure; the operand field of the condition is set to #f.

condition type+: condition-type:inapplicable-object datum operands
This type indicates an error in which a program attempted to apply an object that is not a procedure. The object being applied is saved in the datum field, and the arguments being passed to the object are saved as a list in the operands field.

 
(3 4)                                   error-->
;The object 3 is not applicable.
;To continue, call RESTART with an option number:
; (RESTART 2) => Specify a procedure to use in its place.
; (RESTART 1) => Return to read-eval-print level 1.

condition type+: condition-type:file-error filename
This is an abstract type. It indicates that an error associated with a file has occurred. For example, attempting to delete a nonexistent file will signal an error. The filename field contains a filename or pathname associated with the operation that failed.

condition type+: condition-type:file-operation-error filename verb noun reason operator operands
This is the most common condition type for file system errors. The filename field contains the filename or pathname that was being operated on. The verb field contains a string which is the verb or verb phrase describing the operation being performed, and the noun field contains a string which is a noun or noun phrase describing the object being operated on. The reason field contains a string describing the error that occurred. The operator field contains the procedure performing the operation (or a symbol naming that procedure), and the operands field contains a list of the arguments that were passed to that procedure. For example, an attempt to delete a nonexistent file would have the following field values:

 
filename        "/zu/cph/tmp/no-such-file"
verb            "delete"
noun            "file"
reason          "no such file or directory"
operator        file-remove
operands        ("/zu/cph/tmp/no-such-file")

and would generate a message like this:

 
(delete-file "/zu/cph/tmp/no-such-file") error-->
;Unable to delete file "/zu/cph/tmp/no-such-file" because:
; No such file or directory.
;To continue, call RESTART with an option number:
; (RESTART 3) => Try to delete the same file again.
; (RESTART 2) => Try to delete a different file.
; (RESTART 1) => Return to read-eval-print level 1.

procedure+: error:file-operation-error filename verb noun reason operator operands
This procedure signals a condition of type condition-type:file-operation-error. The fields of the condition are filled in from the corresponding arguments to the procedure.

condition type+: condition-type:derived-file-error filename condition
This is another kind of file error, which is generated by obscure file-system errors that do not fit into the standard categories. The filename field contains the filename or pathname that was being operated on, and the condition field contains a condition describing the error in more detail. Usually the condition field contains a condition of type condition-type:system-call-error.

procedure+: error:derived-file filename condition
This procedure signals a condition of type condition-type:derived-file-error. The filename and condition fields of the condition are filled in from the corresponding arguments to the procedure.

condition type+: condition-type:port-error port
This is an abstract type. It indicates that an error associated with a I/O port has occurred. For example, writing output to a file port can signal an error if the disk containing the file is full; that error would be signalled as a port error. The port field contains the associated port.

condition type+: condition-type:derived-port-error port condition
This is a concrete type that is signalled when port errors occur. The port field contains the port associated with the error, and the condition field contains a condition object that describes the error in more detail. Usually the condition field contains a condition of type condition-type:system-call-error.

procedure+: error:derived-port port condition
This procedure signals a condition of type condition-type:derived-port-error. The port and condition fields of the condition are filled in from the corresponding arguments to the procedure.

condition type+: condition-type:variable-error location environment
This is an abstract type. It indicates that an error associated with a variable has occurred. The location field contains the name of the variable, and the environment field contains the environment in which the variable was referenced.

condition type+: condition-type:unbound-variable location environment
This type is generated when a program attempts to access or modify a variable that is not bound. The location field contains the name of the variable, and the environment field contains the environment in which the reference occurred.

 
foo                                     error-->
;Unbound variable: foo
;To continue, call RESTART with an option number:
; (RESTART 3) => Specify a value to use instead of foo.
; (RESTART 2) => Define foo to a given value.
; (RESTART 1) => Return to read-eval-print level 1.

condition type+: condition-type:unassigned-variable location environment
This type is generated when a program attempts to access a variable that is not assigned. The location field contains the name of the variable, and the environment field contains the environment in which the reference occurred.

 
foo                                     error-->
;Unassigned variable: foo
;To continue, call RESTART with an option number:
; (RESTART 3) => Specify a value to use instead of foo.
; (RESTART 2) => Set foo to a given value.
; (RESTART 1) => Return to read-eval-print level 1.

condition type+: condition-type:arithmetic-error operator operands
This is an abstract type. It indicates that a numerical operation was unable to complete because of an arithmetic error. (For example, division by zero.) The operator field contains the procedure that implements the operation (or a symbol naming the procedure), and the operands field contains a list of the arguments that were passed to the procedure.

condition type+: condition-type:divide-by-zero operator operands
This type is generated when a program attempts to divide by zero. The operator field contains the procedure that implements the failing operation (or a symbol naming the procedure), and the operands field contains a list of the arguments that were passed to the procedure.

 
(/ 1 0)
;Division by zero signalled by /.
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.

procedure+: error:divide-by-zero operator operands
This procedure signals a condition of type condition-type:divide-by-zero. The operator and operands fields of the condition are filled in from the corresponding arguments to the procedure.

condition type+: condition-type:floating-point-overflow operator operands
This type is generated when a program performs an arithmetic operation that results in a floating-point overflow. The operator field contains the procedure that implements the operation (or a symbol naming the procedure), and the operands field contains a list of the arguments that were passed to the procedure.

condition type+: condition-type:floating-point-underflow operator operands
This type is generated when a program performs an arithmetic operation that results in a floating-point underflow. The operator field contains the procedure that implements the operation (or a symbol naming the procedure), and the operands field contains a list of the arguments that were passed to the procedure.

condition type+: condition-type:primitive-procedure-error operator operands
This is an abstract type. It indicates that an error was generated by a primitive procedure call. Primitive procedures are distinguished from ordinary procedures in that they are not written in Scheme but instead in the underlying language of the Scheme implementation. The operator field contains the procedure that implements the operation (or a symbol naming the procedure), and the operands field contains a list of the arguments that were passed to the procedure.

condition type+: condition-type:system-call-error operator operands system-call error-type
This is the most common condition type generated by primitive procedures. A condition of this type indicates that the primitive made a system call to the operating system, and that the system call signalled an error. The system-call error is reflected back to Scheme as a condition of this type, except that many common system-call errors are automatically translated by the Scheme implementation into more useful forms; for example, a system-call error that occurs while trying to delete a file will be translated into a condition of type condition-type:file-operation-error. The operator field contains the procedure that implements the operation (or a symbol naming the procedure), and the operands field contains a list of the arguments that were passed to the procedure. The system-call and error-type fields contain symbols that describe the specific system call that was being made and the error that occurred, respectively; these symbols are completely operating-system dependent.

condition type+: condition-type:control-error
This is an abstract type. It describes a class of errors relating to program control flow.

condition type+: condition-type:no-such-restart name
This type indicates that a named restart was not active when it was expected to be. Conditions of this type are signalled by several procedures that look for particular named restarts, for example muffle-warning. The name field contains the name that was being searched for.

 
(muffle-warning)                        error-->
;The restart named muffle-warning is not bound.
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.

procedure+: error:no-such-restart name
This procedure signals a condition of type condition-type:no-such-restart. The name field of the condition is filled in from the corresponding argument to the procedure.

condition type+: condition-type:not-loading
A condition of this type is generated when the procedure current-load-pathname is called from somewhere other than inside a file being loaded.

 
(current-load-pathname)                 error-->
;No file being loaded.
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.

condition type+: condition-type:warning
This is an abstract type. All warnings should inherit from this type. Warnings are a class of conditions that are usually handled by informing the user of the condition and proceeding the computation normally.

condition type+: condition-type:simple-warning message irritants
This is the condition generated by the warn procedure. The fields message and irritants are taken directly from the arguments to warn; message contains an object (usually a string) and irritants contains a list of objects. The reporter for this type uses format-error-message to generate its output from message and irritants.

condition type+: condition-type:simple-condition message irritants
This is an unspecialized condition that does not fall into any of the standard condition classes. The message field contains an object (usually a string) and irritants contains a list of objects. The reporter for this type uses format-error-message to generate its output from message and irritants.

condition type+: condition-type:breakpoint environment message prompt
A condition of this type is generated by the breakpoint mechanism. The contents of its fields are beyond the scope of this document.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Chris Hanson on July, 18 2001 using texi2html