Unwind-protect in portable Scheme

 Dorai Sitaram 
 Download uwcallcc.tar.gz 



In a web article [4], Kent Pitman, one of the authors of the Scheme standard [3], observes that Scheme's non-local control operator call/cc is misdesigned because it thwarts the creation of a pragmatic unwind-protect facility that would have worked in the context of Scheme. To solve this problem, he proposes that one of two modified versions of call/cc should have been provided in the Scheme standard, which would then permit a suitable unwind-protect, while still providing full continuations.

This text will show that we can indeed implement these modified call/ccs and their companion unwind-protect in standard Scheme. The approach follows that specified in ``Constraining Control'', a 1985 paper by Daniel Friedman and Christopher Haynes [2]. In it they note that raw call/cc is too powerful to use unaltered in all programming scenarios, but that it can be constrained quite easily. In essence, they define new call/cc operators that call the original call/cc, but instead of directly calling the call/cc-argument on the continuation, they call it on a continuation object or cob, which is a procedure that performs whatever additional constraining tasks are required before calling the actual continuation.1

Indeed, Friedman and Haynes already tackle the problem of specifying an unwind-protect for Scheme, and observe that because Scheme's continuations can be called after the context that defined them has exited, there is a choice of meaningful unwind-protect semantics. They recognize at least four semantics, and proceed to implement one. The two proposed by Pitman aren't in their four, but can be obtained using the same cob tactics that they illustrate.

Let us clearly identify what is given and what the goal is. We have standard Scheme, which has call/cc and dynamic-wind.2 We assume a fluid-let form that works correctly in the presence of call/cc. While fluid-let isn't standard, it can be defined as a macro.3

For each of the two proposals, we will define a new call/cc and a corresponding unwind-protect that works correctly with it.4

Section 1 defines an unwind-protect that recognizes escaping continuations; Section 2 defines one that recognizes last-use continuations.


1 Once these new operators are defined, the original call/cc would have to be retired so as not to interfere with the functioning of the new operators. This can be done using Scheme's lexical scoping and assignment. A good argument for why this retirement should be done explicitly and not inherently in the standard is that the present modification is but one of many possible, and we should leave the door open for a different modification.

2 Only unary continuations are considered. I will ignore the presence of Scheme's multiple values, as they tend to add bulk, not illumination, to the code.

3 (fluid-let ((x v) ...) e ...) expands to

(let ((other-x v) ...)
  (let ((swap (lambda ()
                (let ((temp other-x))
                  (set! other-x x)
                  (set! x temp))
                ...)))
    (dynamic-wind swap 
                  (lambda () e ...)
                  swap)))

Expression of this macro with Scheme's syntax-rules or syntax-case is a bit long-winded though quite possible, and is left as an exercise.

4 For convenience, we will define an unwind-protect-proc procedure, and then define the unwind-protect form in terms of the procedure.

Last modified: Thurs, May 15, 2003, 9:52 am US/Eastern
HTML conversion by TeX2page 4r9a