; Generic implementation of all four delimited control operators ; shift/reset, prompt/control, shift0/reset0 and prompt0/control0 ; aka. -F- through +F+ ; ; The code below is parameterized by two boolean flags: ; is-shift and keep-delimiter-upon-effect. ; Each flag is present in the code exactly once, in a trivial ; context. In particular, the difference between shift and control is ; one statement: (hole-push! (cell-new k-return is-shift)) ; which tells whether the created hole is delimiting or not. ; All four combinations of the two flags correspond to four ; delimited control operators, -F- through +F+ ; ; The code relies on call/cc for capturing undelimited ; continuations, and uses one global mutable cell. It turns out, this ; is sufficient for implementing not only shift/reset (Danvy/Filinski) ; but also for control/prompt and the other F operators. That has not ; been known before. In particular, the implementation of ; control/prompt needs no eq? operations. ; ; This implementation immediately leads to a CPS transform for ; control/prompt (which has not been known before either). That ; transform turns almost identical to the one discussed in the ; forth-coming paper ``A Monadic Framework for Delimited ; Continuations'' by R. Kent Dybvig, Simon Peyton Jones and Amr Sabry ; ; This code is inspired by CC_Ref.hs -- contexts with holes, while reading ; Dorai Sitaram and Matthias Felleisen paper (Lisp and symbolic computation, ; 1990) ; A hole has a continuation and a mark. The mark, if #t, says if the hole ; is a delimiting hole. ; Non-delimiting hole is just like the return from a regular function. ; ; $Id: delim-control-n.scm 815 2005-09-05 23:02:12Z oleg $ (module delim-control mzscheme (provide prompt* control* abort prompt control shift reset prompt0 control0 shift0) ; This is one single global mutable cell (define holes '()) (define (hole-push! hole) (set! holes (cons hole holes))) (define (hole-pop!) (let ((hole (car holes))) (set! holes (cdr holes)) hole)) (define (cell-new v mark) (cons v mark)) (define (cell-ref c) (car c)) (define (cell-marked? c) (cdr c)) ; Essentially this is the ``return from the function'' (define (abort-top! v) ((cell-ref (hole-pop!)) v)) (define (unwind-till-marked! keep-delimiter-upon-effect) (if (null? holes) (error "No prompt set")) (let ((hole (hole-pop!))) (if (cell-marked? hole) ; if marked, it's prompt's hole (begin (hole-push! ; put it back (if keep-delimiter-upon-effect hole (cell-new (cell-ref hole) #f))) ; make the hole non-delimiting '()) (cons hole (unwind-till-marked! keep-delimiter-upon-effect))))) (define (prompt* thunk) (call-with-current-continuation (lambda (outer-k) (hole-push! (cell-new outer-k #t)) ; it's prompt's hole (abort-top! (thunk))))) (define (control* is-shift keep-delimiter-upon-effect f) (call-with-current-continuation (lambda (k-control) (let* ((holes-prefix (reverse (unwind-till-marked! keep-delimiter-upon-effect))) (invoke-subcont (lambda (v) (call-with-current-continuation (lambda (k-return) (hole-push! (cell-new k-return is-shift)) (for-each hole-push! holes-prefix) (k-control v)))))) (abort-top! (f invoke-subcont)))))) (define (abort v) (control* #f #t (lambda (k) v))) ; Some syntactic sugar (define-syntax prompt (syntax-rules () ((prompt e) (prompt* (lambda () e))))) (define-syntax control (syntax-rules () ((control f e) (control* #f #t (lambda (f) e))))) ; introduce convenient synonyms (define-syntax reset (syntax-rules () ((reset e) (prompt e)))) (define-syntax shift (syntax-rules () ((shift f e) (control* #t #t (lambda (f) e))))) (define-syntax prompt0 (syntax-rules () ((prompt0 e) (prompt e)))) (define-syntax control0 (syntax-rules () ((control f e) (control* #f #f (lambda (f) e))))) (define-syntax shift0 (syntax-rules () ((shift0 f e) (control* #t #f (lambda (f) e))))))