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

exception systems




Here is a proposal for the core of an exception system, derived
from the Common Lisp condition system and from Kent Pitman's
Not-Quite-Proposal from last September.  To be complete it
needs a specification of standard condition types and restart
tags and which Scheme procedures raise which conditions, as
well as procedures and macros encapsulating common idioms.

The core has three parts: conditions, handlers, and restarts.
A condition contains information about an exceptional situation.
A handler is a procedure that is invoked when a condition is
raised.  A restart is a recovery procedure that can be invoked
by handlers, or anyone else for that matter.

This proposal addresses Kent Pitman's idea that the purpose of
an exception system is to mediate between separate programs
(which isn't suprising; he wrote the Common Lisp proposal on
which this is based).  The specification of a procedure can
include the types of conditions it may raise and the restarts
that it may make available.  Users of the procedure can then
make use of the both procedure's normal and exceptional
functionality.

Conditions are records with multiple inheritance.  Multiple
inheritance is necessary because recovery strategies for several
types of conditions might be appropriate for a particular exceptional
situation.  Multiple inheritance is likely to make field access
slower than for simple records, but conditions are only used in
`rare' circumstances.

Implementation of all this is simple, especially if the condition
procedures do not need to be super fast.  A condition could contain
an a-list of (<type> . #(slot1 slot2 ...)) for example.

                                     -Richard


Conditions ----------------

Conditions are records with multiple inheritance.

(make-condition-type supertypes field-names)

  <Supertypes> is a list of condition types, <field-names> is a list
  of symbols.  The supertypes of the returned condition type are the
  <supertypes> along with the union of thier supertypes.  An instance
  of a condition type has values for the condition-type's fields and
  for the fields of its supertypes.  The <field-names> must be distinct
  from those of the supertypes.

(condition-maker condition-type field-names)

  Returns a procedure that constructs conditions of the given type.
  <Field-names> must include all of the fields of <condition-type>
  and its supertypes.  The arguments to the resulting constructor
  are the values for the condition's fields, in the order listed
  in <field-names>.

(condition-predicate condition-type)

  Returns a predicate that returns #T when passed a condition of
  type <condition-type> or one whose type is a subtypes of
  <condition-type>.

(condition-accessor condition-type field-name)

  Returns a function that, given a condition whose type is <condition-
  type> or a subtype of <condition-type>, will return the value of the
  specified field for that condition.


Signalling and handlers ----------------

(signal condition)

  Invoke the current handler on <condition>.

(with-handler handler thunk)

  Calls <thunk> with no arguments, and, if no condition is signalled,
  returns the values that <thunk> returns.

  If a condition is signalled during the execution of <thunk> it is
  passed to <handler>, which should be a procedure of one argument.
  If <handler> declines to handle the condition it should return a
  condition which will then be passed to the handler in place at the
  time of the call to WITH-HANDLER.  Signalling a condition does not
  change the currently installed handler.


Restarts ----------------

(make-restart-tag id) -> restart-tag

  <Id> is used for printing the tag.

(call-with-restart restart-tag predicate restart-procedure
		   [discloser [interactive-procedure]]
		   thunk)

  Invokes <thunk> in such a way that if a condition accepted by
  <predicate> is signalled, a restart containing <restart-procedure>
  will be visible.  <Discloser>  is passed an output port and explains
  the restart in natural language.  <Interactive-procedure> is passed
  an output port and an input port, prompts the user, and procedes
  using the result supplied.  The <restart-procedure> and <interactive-
  procedure> are invoked with the continuation of the call to
  CALL-WITH-RESTART.

  A suitable macro could be used to make calls to CALL-WITH-RESTART
  more readable.

(restart? restart)              tests for a restart
(interactive-restart? restart)  tests for an interactive restart

(find-restarts restart-tag condition) -> list of restarts

  Returns any currently available restarts for <restart-tag> whose
  predicate accepts <condition>.

(invoke-restart restart . arguments)

  Calls <restart>'s restart procedure on <arguments>.

(invoke-restart-interactively restart input-port output-port)

  Calls <restart>'s interactive procedure.

(disclose-restart restart port)

  Calls <restart>'s discloser on <port>.


As an example of how restarts can be used, here is a definition
of function that adds one to integers.  It assumes that there is
an `interactive' restart tag for restarts that require user
intervention and a condition type for wrong-type-arguments.

(define (add-one x)
  (if (integer? x)
      (+ x 1)
      (call-with-restart interactive             ; tag
			 wrong-type-argument?    ; predicate
			 add-one                 ; restart procedure
			 (lambda (out)           ; discloser
			   (display "Supply a new argument ADD-ONE and proceed"
				    out))
			 (lambda (in out)        ; interactive procedure
			   (display "New argument to ADD-ONE: " out)
			   (add-one (read in)))
			 (lambda ()
			   (signal (make-wrong-type-argument-condition
				      add-one
				      x))))))