Condition system
================

As of version 1.3 (different from all older versions), Scheme48 supports two different condition systems.  One of them, the original one, is a simple system where conditions are represented as tagged lists.  This section documents the original one.  The new condition system is [SRFI 34, 35], and there is a complicated translation layer between the old one, employed by the run-time system, and the new one, which is implemented in a layer high above that as a library, but a library which is always loaded in the usual development environment. See the [SRFI 34, 35] documents for documentation of the new condition system.  [SRFI 34] is available from the exceptions structure; SRFI 35, from the conditions structure.

Note: The condition system changed in Scheme48 version 1.3. While the old one is still available, the names of the structures that implement it changed.  Signals is now simple-signals, and conditions is now simple-conditions.  The structure that signals now names implements the same interface, but with [SRFI 34, 35] underlying it.  The structure that the name conditions now identifies [SRFI 35].  You will have to update all old code that relied on the old signals and conditions structure either by using those structures' new names or by invasively modifying all code to use [SRFI 34, 35].  Also, the only way to completely elide the use of the SRFIs is to evaluate this in an environment with the exceptions-internal and vm-exceptions structure open:

     (begin (initialize-vm-exceptions! really-signal-condition)
            ;; INITIALIZE-VM-EXCEPTIONS! returns a very large object,
            ;; which we probably don't want printed at the REPL.
            #t) lisp

Signalling, handling, and representing conditions
-------------------------------------------------

Scheme48 provides a simple condition system. (Note, however, that Scheme48's condition system is likely to be superseded in the near future by [SRFI 34, SRFI 35].)  Conditions are objects that describe exceptional situations. Scheme48 keeps a registry of condition types, which just have references to their supertypes. Conditions are simple objects that contain only two fields, the type and the type-specific data (the stuff).  Accessor procedures should be defined for particular condition types to extract the data contained within the `stuff' fields of instances of of those condition types.  Condition types are represented as symbols.  Condition handlers are part of the system's dynamic context; they are used to handle exceptional situations when conditions are signalled that describe such exceptional situations.  Signalling a condition signals that an exceptional situation occurred and invokes the current condition handler on the condition.

Scheme48's condition system is split up into three structures:

simple-signals
     Exports procedures to signal conditions and construct conditions, as well as some utilities for common kinds of conditions.

handle
     Exports facilities for handling signalled conditions.

simple-conditions
     The system of representing conditions as objects.

The simple-signals structure exports these procedures:

 -- procedure: make-condition type-name stuff returns condition
     The condition object constructor.

 -- procedure: signal-condition condition returns values (may not return)
 -- procedure: signal type-name stuff ... returns values (may not return)
     Signal-condition signals the given condition.  Signal is a convenience atop the common conjunction of signal-condition and make-condition: it constructs a condition with the given type name and stuff, whereafter it signals that condition with signal-condition.

 -- procedure: error message irritant ... returns values (may not return)
 -- procedure: warn message irritant ... returns values (may not return)
 -- procedure: syntax-error message irritant ... returns expression (may not return)
 -- procedure: call-error message irritant ... returns values (may not return)
 -- procedure: note message irritant ... returns values (may not return)
     Conveniences for signalling standard condition types.  These procedures generally either do not return or return an unspecified value, unless specified to by a user of the debugger.  Syntax-error returns the expression (quote syntax-error), if the condition handler returns to syntax-error in the first place.

     By convention, the message should be lowercased (ie the first word should not be capitalized), and it should not end with punctuation. The message is typically not a complete sentence.  For example, these all follow Scheme48's convention:

       argument type error

       wrong number of arguments

       invalid syntax

       ill-typed right-hand side

       out of memory, unable to continue

     These, on the other hand, do not follow the convention and should be avoided:

       Argument type error:

       An argument of the wrong type was passed.

       possible type mismatch:

       Luser is an idiot!

     Elaboration on a message is performed usually by wrapping an irritant in a descriptive list.  For example, one might write:

          (error "invalid argument"
                 '(not a pair)
                 `(while calling ,frobbotz)
                 `(received ,object)) lisp

          This might be printed as:

               Error: invalid argument
                      (not a pair)
                      (while calling #{Procedure 123 (frobbotz in ...)})
                      (received #(a b c d)) example

The handle structure exports the following procedures:

 -- procedure: with-handler handler thunk returns values
     Sets up HANDLER as the condition handler for the dynamic extent of THUNK.  HANDLER should be a procedure of two arguments: the condition that was signalled and a procedure of zero arguments that propagates the condition up to the next dynamically enclosing handler. When a condition is signalled, HANDLER is tail-called from the point that the condition was signalled at.  Note that, because HANDLER is tail-called at that point, it will return to that point also.

     Warning: With-handler is potentially very dangerous. If an exception occurs and a condition is raised in the handler, the handler itself will be called with that new condition!  Furthermore, the handler may accidentally return to an unexpecting signaller, which can cause very confusing errors.  Be careful with with-handler; to be perfectly safe, it might be a good idea to throw back out to where the handler was initially installed before doing anything:

          ((call-with-current-continuation
             (lambda (k)
               (lambda ()
                 (with-handler (lambda (c propagate)
                                 (k (lambda () HANDLER BODY)))
                   (lambda () BODY)))))) lisp

 -- procedure: ignore-errors thunk returns values or condition
 -- procedure: report-errors-as-warnings thunk message irritant ... returns values
     Ignore-errors sets up a condition handler that will return error conditions to the point where ignore-errors was called, and propagate all other conditions.  If no condition is signalled during the dynamic extent of THUNK, ignore-errors simply returns whatever THUNK returned.  Report-errors-as-warnings downgrades errors to warnings while executing THUNK.  If an error occurs, a warning is signalled with the given message, and a list of irritants constructed by adding the error condition to the end of the list IRRITANT ....

Finally, the simple-conditions structure defines the condition type system.  (Note that conditions themselves are constructed only by make-condition (and signal) from the simple-signals structure.)  Conditions are very basic values that have only two universally defined fields: the type and the stuff. The type is a symbol denoting a condition type.  The type is specified in the first argument to make-condition or signal.  The stuff field contains whatever a particular condition type stores in conditions of that type.  The stuff field is always a list; it is created from the arguments after the first to make-condition or signal.  Condition types are denoted by symbols, kept in a global registry that maps condition type names to their supertype names.

 -- procedure: define-condition-type name supertype-names returns unspecified
     Registers the symbol NAME as a condition type.  Its supertypes are named in the list SUPERTYPE-NAMES.

 -- procedure: condition-predicate ctype-name returns predicate
     Returns a procedure of one argument that returns #t if that argument is a condition whose type's name is CTYPE-NAME or #f if not.

 -- procedure: condition-type condition returns type-name
 -- procedure: condition-stuff condition returns list
     Accessors for the two immutable fields of conditions.

 -- procedure: error? condition returns boolean
 -- procedure: warning? condition returns boolean
 -- procedure: note? condition returns boolean
 -- procedure: syntax-error? condition returns boolean
 -- procedure: call-error? condition returns boolean
 -- procedure: read-error? condition returns boolean
 -- procedure: interrupt? condition returns boolean
     Condition predicates for built-in condition types.

 -- procedure: make-exception opcode reason arguments returns exception
 -- procedure: exception? condition returns boolean
 -- procedure: exception-opcode exception returns integer-opcode
 -- procedure: exception-reason exception returns symbol
 -- procedure: exception-arguments exception returns list
     Exceptions represent run-time errors in the Scheme48 VM.  They contain information about what opcode the VM was executing when it happened, what the reason for the exception occurring was, and the relevant arguments.

Displaying conditions
---------------------

The display-conditions structure is also relevant in this section.

 -- procedure: display-condition condition port returns unspecified
     Prints CONDITION to PORT for a user to read.  For example:

          (display-condition (make-condition 'error
                               "Foo bar baz"
                               'quux
                               '(zot mumble: frotz))
                             (current-output-port))
              -| Error: Foo bar baz
              -|        quux
              -|        (zot mumble: frotz) lisp

 -- method table: &disclose-condition condition returns disclosed
     Method table (see Generic dispatch system) for a generic procedure (not exposed) used to translate a condition object into a more readable format.  See Writer.

 -- procedure: limited-write object port max-depth max-length returns unspecified
     A utility for avoiding excessive output: prints OBJECT to PORT, but will never print more than MAX-LENGTH of a subobject's components, leaving a --- after the last component, and won't recur further down the object graph from the vertex OBJECT beyond MAX-DEPTH, instead printing an octothorpe (#).

          (let ((x (cons #f #f)))
            (set-car! x x)
            (set-cdr! x x)
            (limited-write x (current-output-port) 2 2))
              -| ((# # ---) (# # ---) ---) lisp