; This is a simple implementation of UNWIND-PROTECT and one-shot ; continuations in portable Scheme, written by William D Clinger. ; ; For a simpler implementation that implements the semantics of ; UNWIND-PROTECT as in Common Lisp, which I regard as a more useful ; semantics, see http://www.ccs.neu.edu/home/will/UWESC/unwind.sch ; ; Implementing these things in portable Scheme is no big deal, so ; I should explain the history of this code. ; ; History. ; ; In 1985, Dan Friedman and Chris Haynes showed how to implement ; UNWIND-PROTECT in terms of a simple and unconstrained version of ; Scheme's CALL-WITH-CURRENT-CONTINUATION procedure [FH85]. ; Their algorithm became the basis for most implementations of ; Scheme's now-standard DYNAMIC-WIND procedure, which serves as a ; portable and modular mechanism for implementing advanced control ; structures that exploit the power of Scheme's continuations while ; constraining them in appropriate ways. ; ; In particular, DYNAMIC-WIND makes it easy to implement any of ; several variations on Common Lisp's UNWIND-PROTECT. ; ; Despite the fact that many people had implemented UNWIND-PROTECT ; in portable Scheme, Kent Pitman began to claim it couldn't be ; done. When no one paid any attention to him, he made this ; argument on his web site [KP], and ignored all criticism of his ; arguments. In particular, he didn't bother to read the relevant ; papers that I and others cited for him, and he rejected an ; implementation similar to the one shown below because it didn't ; have exactly the right interaction between UNWIND-PROTECT and ; one-shot continuations that Kent had in mind. ; ; Eventually Dorai Sitaram was able to guess the semantics that ; Kent had in mind, and implemented it using the Friedman & Haynes ; technique---partly as a tutorial in that technique, and partly ; to refute Pitman's claim that it couldn't be done [DS03]. ; ; I simplified Sitaram's code by using DYNAMIC-WIND, but I didn't ; simplify it as much as I could have [WC]. In particular, Sitaram's ; implementation redefined Scheme's CALL-WITH-CURRENT-CONTINUATION ; procedure, and did not interoperate correctly with continuations ; that had been created by that procedure prior to its redefinition. ; Although I did not redefine any of Scheme's standard procedures, ; I deliberately preserved the interoperation misfeature, while ; writing the code in a way that I thought would make it easy for ; people to see how that misfeature can be removed. ; ; My preservation and highlighting of this misfeature was a private ; joke, taking revenge on Pitman for his prior rejection of simpler ; and better implementations. Alas, both Sitaram and Pitman cited ; that joke as though it were a serious implementation [DS03,KP]. ; That created a situation in which Sitaram's tutorial and my joke ; implementations were being cited more than serious implementations. ; I hope this implementation will address that. ; ; For a much simpler implementation that implements the semantics ; of UNWIND-PROTECT as in Common Lisp, which I regard as a more ; useful semantics, see [WC2]. ; ; I apologize for my part in this nonsense. ; ; [FH85] Daniel P Friedman and Christopher T Haynes. ; Constraining control. POPL 1985, pages 245-254. ; ; [KP] Kent Pitman. UNWIND-PROTECT versus continuations. Online at ; http://www.nhplace.com/kent/PFAQ/unwind-protect-vs-continuations.html ; ; [DS03] Dorai Sitaram. Unwind-protect in portable Scheme. Fourth ; Workshop on Scheme and Functional Programming, 7 November 2003. ; Online at http://www.ccs.neu.edu/home/dorai/uwcallcc/uwcallcc.html ; ; [WC] William D Clinger. The earlier, joke version of this ; implementation. 15 May 2003. Online at ; http://www.ccs.neu.edu/home/will/Temp/uwesc.sch ; ; [WC2] William D Clinger. The obvious implementation of Common ; Lisp's UNWIND-PROTECT in portable Scheme. Online at ; http://www.ccs.neu.edu/home/will/UWESC/unwind.sch ; ; ; ; Semantics. ; ; The call/cc-escaping procedure. ; ; (call/cc-escaping f) calls f on the current continuation k, with ; the restrictions that k may be invoked only once, that k takes ; exactly one argument, and that k interacts with unwind-protect ; in the special way that is explained below. ; ; The unwind-protect macro. ; ; (unwind-protect ) evaluates to obtain its ; single value v, passing v to a continuation that normally (but ; not always; see below) evaluates for effect, and returns v ; in any case. If the evaluation of results in a throw past ; the natural continuation of , and has not yet been ; evaluated, and the throw is to a continuation that was created by ; call/cc-escaping, then is evaluated for effect during the ; throw, and will not be evaluated again if and when returns ; a result to its continuation. If the throw is to a continuation ; created by call-with-current-continuation, however, then ; is not evaluated. Furthermore no continuation that was created ; by call-with-current-continuation is allowed to continue the ; evaluation of once has been evaluated. ; ; (Rationale: This is Kent Pitman's semantics, for the variant that ; Dorai Sitaram calls call/cc-e [KP,DS03]. It may not be exactly ; the semantics that Kent had in mind, but it's pretty close, and ; it shouldn't be hard to repair any defects that come to light.) ; ; ; Implementation notes. ; ; DYNAMIC-WIND does most of what we want, but we have to provide a way ; for continuations to communicate with the preludes and postludes of ; a DYNAMIC-WIND. The required communications are: ; ; 1. When a continuation is invoked, it must broadcast whether it is ; an escaping (one-shot) or full continuation. ; 2. An escaping continuation can be invoked only once. ; 3. An UNWIND-PROTECT that has been exited must not allow any full ; continuations that were captured within its body to be invoked. ; ; All three of these communications can be implemented using a single ; boolean variable that is shared by all continuation objects and ; cleanup forms. This variable will be true when a throw to a full ; continuation is being performed, and false otherwise. ; ; This code doesn't allow multiple return values to be passed to an ; escaping continuation or returned by an UNWIND-PROTECT. Both are ; easy to fix, but would complicate the implementation in uninteresting ; ways so I will leave that to the reader. Similarly, this code does ; not protect against the possibility that a prelude or postlude will ; perform a throw that leaves the full? variable in its false state. ; That, too, is easy to fix and is left as an exercise for the reader. ; These two variables will be assigned their real values later. (define call/cc-escaping (lambda (f) (f (lambda (v) v)))) (define unwind-protect-proc (lambda (body postlude) (body) (postlude))) (let ((full? #t)) ; will be true when throwing to a full continuation (define (errorproc . args) ; Restore the global variable to its normal state. (set! full? #t) (error "You tried to call an escaping continuation twice")) (set! call/cc-escaping (lambda (f) (let ((v (call-with-current-continuation (lambda (escape) (f (lambda (v) (let ((k escape)) (set! escape errorproc) (set! full? #f) (k v)))))))) (set! full? #t) v))) (set! unwind-protect-proc (lambda (body postlude) (let ((exited? #f) ; have we done the postlude? (normal-exit? #f)) ; is this a normal exit? (dynamic-wind (lambda () (if (and exited? full?) (errorproc))) (lambda () (let ((v (body))) (set! normal-exit? #t) v)) (lambda () (if (and (not exited?) (or normal-exit? (not full?))) (begin (set! exited? #t) (postlude)))))))) (if #f #f)) (define-syntax unwind-protect (syntax-rules () ((unwind-protect body cleanup ...) (unwind-protect-proc (lambda () body) (lambda () cleanup ...))))) ; Modification history. ; ; 3 February 2005 -- original version ; 3 February 2005 -- several bug fixes, last modified at 19:27 EDT ; 7 February 2005 -- URL added for the simpler and more useful semantics