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

Proposal for exception handling



The following is a proposal for exception handling in Scheme.  The
mechanism is straightforward and requires very little effort to
implement, yet supports a variety of exception handling disciplines.
Please respond directly to the mailing list with any comments or
questions.  An HTML version of this proposal is in the Scheme repository as

  http://www.cs.indiana.edu/scheme-repository/doc.proposals.exceptions.html.

A portable implementation of the system along with a small suite of
test programs is available via ftp as

  ftp.cs.indiana.edu:pub/scheme-repository/doc/prop/exceptions.ss  
 

Dan, Chris, and Kent


A PROPOSAL FOR EXCEPTION HANDLING IN SCHEME

Dan Friedman, Chris Haynes, and Kent Dybvig
September 4, 1995

An exception is raised by the system whenever an error is to be signaled
or whenever the system determines that evaluation cannot proceed in a
manner consistent with the semantics of Scheme.  An exception may also
be raised explicitly by a program.  Any object may denote an
exception.  Whenever an exception is raised, the current exception
handler is invoked with the exception as its only argument.  Any
procedure accepting one argument may serve as an exception handler.
The denotation of system exceptions and the initial current exception
handler are system-dependent, as is the behavior whenever a handler for
an exception raised by the system returns to its continuation.

The following two procedures and one syntactic form are supported.


(raise exn)                                                     procedure

Invokes the current exception handler with exn.


(current-exception-handler [handler])                           procedure

When invoked without arguments, the current exception handler is
returned.  When invoked with a handler argument, handler is installed
as the current exception handler and an unspecified value is returned.


(with-handlers ((predicate handler) ...) e1 e2 ...)          derived form

The predicate and handler expressions are evaluated and used to create
the current exception handler for the dynamic context in which
expressions e1, e2, ... are evaluated, in sequence.  The value of the
last expression is returned.  When the new current exception handler is
invoked with exn, the predicates are invoked, in sequence, with exn
until a true value is returned.  The predicates are called in a dynamic
context in which the current exception handler is the exception handler
that was current in the dynamic context of the call to with-handlers.
If one of the predicates returns a true value, the corresponding
handler is selected.  If none of the predicates returns a true value,
the handler that was current in the dynamic context of the
with-handlers call is selected.  The selected handler is invoked with
exn in the continuation of the with-handlers expression.


RATIONALE

The intent of this exception system is to provide a simple and safe
mechanism that is satisfactory for most purposes, with enough flexibility
to allow other forms of exception handling to be accomplished in a
straightforward manner.

Though system exceptions have been left unspecified, it is hoped that the
more common ones will eventually be standardized.

raise and with-handlers may be implemented as follows:

(define raise
  (lambda (exn)
    ((current-exception-handler) exn)))

(define-syntax with-handlers
  (syntax-rules ()
    ((_ ((predicate handler-procedure) ...) b1 b2 ...)
     ((call-with-current-continuation
        (lambda (k)
          (parameterize ((current-exception-handler
                          (let ((rh (current-exception-handler))
                                (preds (list predicate ...))
                                (handlers (list handler-procedure ...)))
                            (lambda (exn)
                              (parameterize ((current-exception-handler rh))
                                (let f ((preds preds) (handlers handlers))
                                  (if (not (null? preds))
                                      (if ((car preds) exn)
                                          (k (lambda () ((car handlers) exn)))
                                          (f (cdr preds) (cdr handlers))))))
                              (rh exn)))))
            (call-with-values
              (lambda () b1 b2 ...)
              (lambda args (k (lambda () (apply values args))))))))))))

where parameterize is defined as follows:

(define-syntax parameterize
  ; simplified version limited to one parameter / value "binding"
  (syntax-rules ()
    ((_ ((x v)) e1 e2 ...)
     (let ((p x) (y v))
       (let ((swap (lambda () (let ((t (p))) (p y) (set! y t)))))
         (dynamic-wind swap (lambda () e1 e2 ...) swap))))))

Although the proposed interface is given in its entirety above, we show
below two possible variations on with-handlers.  Both may be defined by
the programmer.  They demonstrate some of the power of the system.

with-continuing-handlers is like with-handlers, except that handlers
return to the continuation of the handler call, rather than to the
continuation of the with-continuing-handler expression.

(define-syntax with-continuing-handlers
  (syntax-rules ()
    ((_ ((predicate handler-procedure) ...) b1 b2 ...)
     ((call-with-current-continuation
        (lambda (k)
          (parameterize
            ((current-exception-handler
               (let ((rh (current-exception-handler))
                     (preds (list predicate ...))
                     (handlers (list handler-procedure ...)))
                 (lambda (exn)
                   (call-with-current-continuation
                     (lambda (k1)
                       (parameterize ((current-exception-handler rh))
                         (let f ((preds preds) (handlers handlers))
                           (if (not (null? preds))
                               (if ((car preds) exn)
                                   (k (lambda ()
                                        (k1 ((car handlers) exn))))
                                   (f (cdr preds) (cdr handlers))))))
                       (rh exn)))))))
            (call-with-values
              (lambda () b1 b2 ...)
              (lambda args (k (lambda () (apply values args))))))))))))

with-rec-handlers is like with-handlers, except that handlers are
invoked in a dynamic context in which the current exception handler is
the one established by the with-rec-handlers expression.

(define-syntax with-rec-handlers
  (syntax-rules ()
    ((_ ((predicate handler-procedure) ...) b1 b2 ...)
     ((call-with-current-continuation
        (lambda (k)
          (letrec
            ((new-rh
               (let ((rh (current-exception-handler))
                     (preds (list predicate ...))
                     (handlers (list handler-procedure ...)))
                 (lambda (exn)
                   (parameterize ((current-exception-handler rh))
                     (let f ((preds preds) (handlers handlers))
                       (if (not (null? preds))
                           (if ((car preds) exn)
                               (k (lambda ()
                                    (parameterize ((current-exception-handler
                                                     new-rh))
                                      ((car handlers) exn))))
                               (f (cdr preds) (cdr handlers))))))
                   (rh exn)))))
            (parameterize ((current-exception-handler new-rh))
              (call-with-values
                (lambda () b1 b2 ...)
                (lambda args (k (lambda () (apply values args)))))))))))))