Two useful extensions to standard Scheme

Receive-expressions

(receive <formals> <expression> <body>)   library syntax

<Formals>, <expression>, and <body> are as described in R5RS. Specifically, <formals> can have any of three forms:

In any case, the expressions in <body> are evaluated sequentially in the extended environment. The results of the last expression in the body are the values of the receive-expression.

Implementation

The receive-expression syntax can be introduced into Scheme by the following syntax definition:

(define-syntax receive
  (syntax-rules ()
    ((receive formals expression body ...)
     (call-with-values (lambda () expression)
                       (lambda formals body ...)))))

Minimal exception handling

Adding a mechanism for raising and handling exceptions to Scheme takes a little more work. Fortunately, exceptions in Algorithms for functional programming arise rarely and can be handled in elementary ways, so we'll build only a minimal mechanism, free from most bells and whistles. In particular, we'll assume that it is unnecessary to divide exceptions into subcategories and to search through a stack of handlers each time an exception is raised, to find one that can deal with the particular kind of exception that has arisen. Instead, we'll simply fetch and apply the most recently activated handler.

The user interface to the exception-handling mechanism consists of a procedure, signal, that raises an exception when invoked, and a new type of expression, the handle-exceptions-expression, that encloses a sequence of expressions to be evaluated with a specified exception-handler in place.

(signal object)   procedure

Raises an exception, passing <object> to the current exception handler.

(handle-exceptions <identifier> <expression> <body>)   library syntax

<Body> is evaluated sequentially. If an exception is raised, the value passed to the exception is bound to <variable>, <expression> is evaluated, and its results are the results of the handle-exceptions-expression. If no exception is raised, the results of the last expression in <body> are the results of the handle-exceptions-expression.

Implementation

The implementation offered here is, approximately, a small subset of the one proposed in 1999 by William Clinger, R. Kent Dybvig, Matthew Flatt, and Marc Feeley in ``SRFI 12: Exception handling''.

We begin with a ``parameter object'': a procedure that guards a storage location, returning its contents when invoked with no arguments and overwriting those contents when invoked with one argument. What is stored in this particular parameter object is a unary procedure: the current exception handler. We give it a default initial value that simply announces that an exception has been raised and prints out the value passed to it by signal:

(define current-exception-handler
  (let ((stored
         (lambda (something)
           (display "An exception has been raised.")
           (newline)
           (display "The signal value was ")
           (display something)
           (display ".")
           (newline))))
    (lambda optional
      (if (null? optional)
          stored
          (set! stored (car optional))))))

The signal procedure fetches the current exception handler and invokes it:

(define (signal signal-value)
  ((current-exception-handler) signal-value))

The with-exception-handler procedure carefully manages the process of setting a new handler in place for the dynamic extent during which a <body> is evaluated and then removing it afterwards. Given the new handler and a zero-argument procedure whose body is the sequence of expressions to be evaluated, it first installs the custom handler, then launches the zero-argument procedure, and finally restores the previous handler. Scheme's dynamic-wind procedure is used to guarantee that the handler applies only when the invocation of the zero-argument procedure is in progress, even if that invocation is interrupted by activations of non-local continuations: The previous handler is restored whenever a non-local continuation is activated, and the new handler is put in place again whenever a continuation within the invocation of the zero-argument procedure is resumed from outside.

(define (with-exception-handler handler thunk)
  (let ((old-handler #f))
    (dynamic-wind (lambda ()
                    (set! old-handler
                          (current-exception-handler))
                    (current-exception-handler handler))
                  thunk
                  (lambda ()
                    (current-exception-handler old-handler)))))

Finally, handle-exceptions is defined as a macro that uses with-exception-handler as the mechanism for managing handlers and Scheme's built-in call-with-current-continuation procedure as the mechanism for escaping from the execution context in which the exception is raised back to the context in which the handle-exceptions-expression is evaluated.

(define-syntax handle-exceptions
  (syntax-rules ()
    ((handle-exceptions variable handle-expression body ...)
     ((call-with-current-continuation
        (lambda (k)
          (with-exception-handler 
            (lambda (variable)
              (k (lambda () handle-expression)))
            (lambda ()
              (call-with-values
                (lambda () body ...)
                (lambda arguments
                  (k (lambda ()
                       (apply values arguments)))))))))))))

Customizing Chez Scheme

Neither DrScheme nor Chez Scheme currently provides receive-expressions or the exception mechanism described here However, Chez Scheme can be configured to load procedure and syntax definitions automatically, in response to a command-line option; if you invoke it (on MathLAN) as

scheme -h /home/stone/courses/algorithms/afs-scheme.heap

you'll get Chez Scheme with these two improvements already loaded.

Incidentally, any user can create a similar heap file containing the compiled versions of procedure and syntax definitions, for efficient loading. Here are the commands I used to create afs-scheme.heap:

$ /net/bin/scheme -s1 afs-scheme.heap
Chez Scheme Version 6.1
Copyright (c) 1998 Cadence Research Systems

> (load "receive.ss")
> (load "exceptions.ss")
> (exit)

On exit, Chez Scheme dumps all the results of compilations performed during the current session into the heap file named as the argument to the -s1 command-line option. The -h command-line option then directs Chez Scheme to recover those results from the heap file during startup.


This document is available on the World Wide Web as

http://www.cs.grinnell.edu/~stone/courses/algorithms/afp-extensions.xhtml

created September 6, 2000
last revised September 6, 2000

John David Stone (stone@cs.grinnell.edu)