Reputation: 49
Why this code
(let ([cc #f]
[pr (make-continuation-prompt-tag 'pr)])
(call-with-continuation-prompt
(λ () (displayln
(+ 2 (call-with-current-continuation
(λ (k) (set! cc k) 1)
pr))))
pr)
(cc 4))
(on Racket v7.5) raise exception?:
3
; continuation application: no corresponding prompt in the current continuation
; Context:
; /usr/share/racket/collects/racket/repl.rkt:11:26
While same code with default tag works as expected:
(let ([cc #f])
(call-with-continuation-prompt
(λ ()
(displayln (+ 2 (call-with-current-continuation
(λ (k) (set! cc k) 1))))))
(cc 4))
3
6
And same (as the first snippet) code with composable continuation
(let ([cc #f]
[pr (make-continuation-prompt-tag 'pr)])
(call-with-continuation-prompt
(λ () (displayln
(+ 2 (call-with-composable-continuation
(λ (k) (set! cc k) 1)
pr))))
pr)
(cc 4))
works as expected too:
3
6
What is wrong with the first snippet? Or what is wrong with my understanding?
Upvotes: 1
Views: 386
Reputation: 10653
From the docs for call-with-current-continuation:
If the continuation argument to proc is ever applied, then it removes the portion of the current continuation up to the nearest prompt tagged by prompt-tag (not including the prompt; if no such prompt exists, the exn:fail:contract:continuation exception is raised), ....
In your first example, when you apply cc
, there is no prompt for pr
in the context ("on the stack") when the application occurs, so it raises an exception.
The second example works because there is always a prompt for the default tag.
The third example works because call-with-composable-continuation
creates a continuation procedure that does not abort the current continuation, so there is no precondition for its application. (That's part of why it's considered a "composable" continuation.)
If it helps, here is approximately how call/cc
can be defined in terms of the abort-current-continuation
and call-with-compposable-continuation
. (Warning: I haven't tested this, so there may be bugs.) In the type annotations below, I use the following conventions: P
is the result type associated with a prompt tag and A
is the result type of a call/cc
or call/comp
call, which is also the type of the continuation's argument. ⊥
is the empty type; it effectively means "doesn't return".
;; call-with-continuation-prompt : (-> P) PromptTag[P] -> P
;; Only allows default abort handler!
;; abort-current-continuation : PromptTag[P] (-> P) -> ⊥
;; Assumes the default abort handler!
;; call-with-composable-continuation : ((A -> P) -> A) PromptTag[P] -> A
;; call/cc : ((A -> ⊥) -> A) PromptTag[P] -> A
(define (call/cc proc tag)
(call-with-composable-continuation
(lambda (ck) ;; ck : A -> P
;; k : A -> ⊥
(define (k v)
(abort-current-continuation tag
(lambda () (ck v))))
(proc k))
tag))
This definition doesn't account for how call/cc
actually interacts with dynamic-wind
, it doesn't work with custom prompt handlers, and it doesn't account for multiple return values (which correspond to multiple continuation arguments), but it should give you a rough idea of what call/cc
is doing. In particular, the call to abort-current-continuation
requires that the current continuation has a prompt tagged with tag
.
Upvotes: 3