(I am not subscribed to this list, so please cc me in replies.)
I have several questions about continuation barriers. I have tested
some examples in PLT Scheme v4.2.4, because that's what I have handy;
if the semantics has changed significantly in Racket, let me know
before wasting time with the rest of this message.
First, is there any document generally describing the rationale for
the design of continuation barriers and advice about when to use them?
Here are two possible implementations of continuation barriers, from
an initial naive (and incorrect) understanding. One precludes
non-local exit; the other also precludes re-entry.
(define (call-with-continuation-barrier procedure)
(dynamic-wind
(let ((once? #f))
(lambda ()
(if once? (error "Re-entry prohibited."))
(set! once? #t)))
procedure
(lambda () 0)))
(define (call-with-continuation-barrier procedure)
(let ((done? #f))
(dynamic-wind
(let ((once? #f))
(lambda ()
(if once? (error "Re-entry prohibited."))
(set! once? #t)))
(lambda () (begin0 (procedure) (set! done? #t)))
(lambda ()
(if (not done?) (error "Non-local exit prohibited."))))))
If only full continuations are involved, no escape-only continuations
or delimited continuations, the latter definition is the correct one,
I believe. However, escape-only continuations and prompt aborts can
cross continuation barriers, but trigger the exit procedure in the
latter definition.
(call-with-current-continuation call-with-continuation-barrier)
;Error!
(call-with-escape-continuation call-with-continuation-barrier)
;OK by PLT Scheme, error by the second definition
(call-with-continuation-prompt
(lambda ()
(call-with-continuation-barrier
(lambda ()
(abort-current-continuation (default-continuation-prompt-tag)
values)))))
;OK by PLT Scheme, error by the second definition
Consequently, there are cases where replacing correct uses of
CALL-WITH-ESCAPE-CONTINUATION by CALL-WITH-CURRENT-CONTINUATION break
a program, which is puzzling to me. Furthermore, the following
definition of CALL-WITH-CURRENT-CONTINUATION is incorrect, because I
can use it to cross continuation barriers that can't be crossed by the
built-in CALL-WITH-CURRENT-CONTINUATION:
(define (call-with-current-continuation procedure)
(call-with-composable-continuation
(lambda (continuation)
(procedure (lambda arguments
(abort-current-continuation
(default-continuation-prompt-tag)
(lambda ()
(apply values arguments))))))))
Can anyone explain to me what is wrong with my definition of
CALL-WITH-CURRENT-CONTINUATION, and why full continuations may not be
used for non-local exits across a continuation barrier, while
escape-only continuations and prompt aborts can be used for exactly
that? I am sure that part of what confuses me is that I don't know
what the general intended use of continuation barriers is.
There is one problem with the definition: it doesn't work if there is
a continuation barrier between the current continuation and the
nearest prompt tag. So here's a nefarious way around that, which I
believe behaves semantically like CALL-WITH-CURRENT-CONTINUATION
(except perhaps for DYNAMIC-WIND execution) but can cross more
continuation barriers:
(define (call-with-nefarious-continuation procedure)
((with-handlers
((exn:fail:contract:continuation?
(lambda (exception)
exception ;ignore
(lambda ()
(call-with-current-continuation procedure)))))
(call-with-composable-continuation
(lambda (composable-continuation)
(call-with-current-continuation
(lambda (full-continuation)
(lambda ()
(procedure
(lambda arguments
(define (return) (apply values arguments))
((with-handlers
((exn:fail:contract:continuation?
(lambda (exception)
exception ;ignore
(lambda ()
(abort-current-continuation
(default-continuation-prompt-tag)
(lambda ()
(composable-continuation return)))))))
(full-continuation return)))))))))))))