(receive <formals> <expression>
<body>) library syntax
<Formals>, <expression>, and <body> are as described in R5RS. Specifically, <formals> can have any of three forms:
(<variable1> ... <variablen>): The
environment in which the receive-expression is evaluated is
extended by binding <variable1>, ...,
<variablen> to fresh locations. The
<expression> is evaluated, and its values are stored into those
locations. (It is an error if <expression> does not have exactly
n values.)
<variable>: The environment in which the
receive-expression is evaluated is extended by binding
<variable> to a fresh location. The <expression> is evaluated,
its values are converted into a newly allocated list, and the list is
stored in the location bound to <variable>.
(<variable1> ... <variablen>
. <variablen + 1>): The environment in which the
receive-expression is evaluated is extended by binding
<variable1>, ..., <variablen + 1>
to fresh locations. The <expression> is evaluated. Its first n
values are stored into the locations bound to <variable1>
... <variablen>. Any remaining values are converted
into a newly allocated list, which is stored into the location bound to
<variablen + 1>. (It is an error if
<expression> does not have at least n values.)
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.
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 ...)))))
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.
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)))))))))))))
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