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.


procedure: (raise exn)

Invokes the current exception handler with exn.


procedure: (current-exception-handler [handler])

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.


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

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)))))))))))))
A full implementation and test suite is available as ftp://ftp.cs.indiana.edu/pub/scheme-repository/doc/prop/exceptions.ss

chaynes@indiana.edu