I'm trying to understand call/cc operator in Scheme. I'm planing of implementing this in my JavaScript lisp. This is my simple code:
(letrec ((x 0)
(f (lambda (r)
(set! x r)
(display (r 20))
(display "10"))))
(display (call/cc f))
(x "30"))
I tough that it should print 20 then 30 then 10. But it create infinite loop (it keep printing 30). How this code should look like to display 3 values, call display 3 times?
Should it be possible to create loops that don't consume stack with continuations?
I've found some example on stack overflow but this one don't work at all:
(define x 0) ; dummy value - will be used to store continuation later
(+ 2 (call/cc (lambda (cc)
(set! x cc) ; set x to the continuation cc; namely, (+ 2 _)
3))) ; returns 5
(x 4) ; returns 6
it freezes the guile interpreter with 100% CPU and it look it waiting for input.
Does you lisp implementation transform user code to continuation passing style? In that case it is easy peasy. call/cc is this:
(define (call/cc& f& continuation)
(define (exit& value actual-continuation)
(continuation value))
(f& exit& continuation))
Looking at your first code I think it becomes something like this:
((lambda (x k)
((lambda (f k)
(call/cc& f (lambda (v) ; continuation a
(display& v (lambda (_) ; continuation b
(x "30" k))))))
(lambda (r k)
(set!& x r (lambda (_) ; continuation c
(r 20 (lambda (v) ; continuation d
(display& v (lambda (_) ; continuation e
(display& "10" k))))))))
k)
0
halt)
Here is whats happening:
We make x and f
call/cc& calls f
x is set to r (continuation a)
r gets called with 20 as value
continuation c is ignore, instead continuation a is called with 20
20 gets displayed, then continuation b gets called
b calls x with "30"
continuation k is ignored, instead continuation a is called with 30
30 gets displayed, then continuation b gets called
go to "b calls x with "30" 3 lines up and continue
So print "20", then "30" forever seems to be the correct result for this code. It's important to notice that it will never display "10" since it calls r and passes the continuation but it gets circumvented to call/cc original continution which is continuation a.
As for implementations. Before it was quite common for all Scheme implementations to just transform the code to continuation passing style, but today it's more common to only do the parts which are required. Eg. Ikarus does not do CPS, but in order for call/cc to work it needs to do it until the next continuation prompt.
It's probably better to look at call/cc without mutation in the beginning. eg.
(+ 2 (call/cc (lambda (exit)
(+ 3 (* 5 (exit 11))))))
Now this turns into:
(call/cc& (lambda (exit k)
(exit 11 (lambda (v)
(*& 5 v (lambda (v)
(+& 3 v k))))))
(lambda (v)
(+& 2 v repl-display)))
Now we know exit gets called and thus this whole thing turns into:
((lambda (v) (+& 2 v repl-display)) 11)
Which displays 13. Now having the continuation as last argument looks good on paper. In an implementation that wants to support varargs it's probably best that the continuation is the first argument.
All continuations are tail calls and thus the stack is never grown. In fact if full CPS is used you never have to return ever. Everything interesting is always passed to the next call until the program halts.
Related
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?
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.
I came across something that I can not understand.
#lang scheme
(define cc #f)
(define (val!)
(call/cc
(lambda (k)
(set! cc k)
0)))
(* 10 (val!))
(cc 100)
So far so good; the continuation of (* 10 []) is stored in cc and if we call (cc 100) we see 1000 in the REPL as expected.
But the next thing I tried was to define a variable to be the result of running the continuation:
(define x (cc 20))
I see 200 as a result in the REPL, but x does not get defined.
Does the continuation stored in cc include its returning so that the call to define never returns and instead the evaluation is the result of (* 10 val)? What is going on?
It returns nothing, because it does not return. (cc 20), just as (cc 100), does not return a value to its caller. cc is not a function, it is a continuation - it remembers where to return / "feed" its value, by itself.
(define x (cc 20))
means, approximately, in pseudocode,
(let ([val (cc 20)])
(primitive-define-top-level-var! "x" val))
but (cc 20) bypasses the setting of val and using it to define x, and returns directly to the top level, as was done by the original captured continuation.
Does the continuation stored in cc include its returning ["destination" -- wn] so that the call to define never returns and instead the evaluation is the result of (* 10 val)?
Yes.
What is going on?
Exactly that.
edit:
Loading the following in DrRacket,
#lang scheme
(define cc #f)
(define (val!)
(call/cc
(lambda (k)
(set! cc k)
0)))
(* 10 (val!))
(cc 100)
(display "Good!\n")
(define x (cc 20))
(display "Shucks!\n")
I even get an error message explaining what is going on:
Language: scheme, with debugging; memory limit: 128 MB.
0
1000
Good!
200
define-values: skipped variable definition;
cannot continue without defining variable
variable: x
in module: 'anonymous-module
>
What's going on?
There are two types of continuations.
A continuation produced by call/cc never returns a value to its caller. See Will Ness's answer for more on that.
But the continuations produced by call-with-composable-continuation are composable continuations, which do return values.
The solution
If you want a continuation to return a value to its caller, you should use a composable continuation, by setting up a prompt and using call-with-composable-continuation.
You can define a kind of prompt:
(define my-prompt
(make-continuation-prompt-tag 'my-prompt))
And use the prompt in call-with-composable-continuation to specify that you only want to capture the continuation starting from the prompt.
(define cc #f)
(define (val!)
(call-with-composable-continuation
(lambda (k)
(set! cc k)
0)
my-prompt))
Then you just have to put the prompt wherever you want the continuation to start before you call val! to save it.
;; the prompt specifies that it's `(* 10 [])`, and not something larger
(call-with-continuation-prompt
(λ () (* 10 (val!)))
my-prompt)
Then, since this continuation has a clear "end" defined by the prompt, it can return a value when it reaches that end.
(define x (cc 20))
; defines x as 200
See also: What exactly is a "continuation prompt?"
I use racket and I got the result 4 for following simple code:
(let/cc done
((let/cc esc
(done (+ 1 (let/cc k
(esc k)))))
3))
and I was going to execute this code step-by-step.
First, I changed the first let/cc into the form of call/cc like below:
(call/cc (λ (done)
((let/cc esc
(done (+ 1 (let/cc k
(esc k)))))
3)))
Of course, this produces 4 also.
Second, since I found the mechanism of call/cc in the internet which says call/cc do following 4 steps:
Captures the current continuation.
Constructs a function C that takes one argument, and applies the current continuation with that argument value.
Passes this function as an argument to expr --- i.e., it invokes (expr C).
Returns the result of evaluating (expr C), unless expr calls C, in which case the value that is passed to C is returned.
Thus, I followed above steps for the first call/cc like:
Current continuation is an identity.
C refers (λ (x) x).
Since expr is (λ (done) ((let/cc esc (done (+ 1 (let/cc k (esc k))))) 3)), (expr C) is:
((λ (done)
((let/cc esc
(done (+ 1 (let/cc k
(esc k)))))
3))
(λ (x) x))
To return the result value of above code, I execute above in racket.
But, above code (modified by me) is not executed and produces an error:
> application: not a procedure;
>
> expected a procedure that can be applied to arguments
>
> given: 4
>
> arguments...:
>
> 3
Please what I did wrong. I'm confusing the concept of continuation. Thanks.
When the interpreter sees a call/cc even the interpreters that doesn't do CPS does it with that subtree. Your code would look something like this:
((λ (done)
((λ (esc)
((λ (k) (esc k))
(λ (r) (k+ done 1 r))))
(λ (v) (v 3))))
values)
; k+ implementation (+, but CPS)
(define (k+ k . args)
(k (apply + args)))
Continuations are not just closures (functions). They also perform a jump to their defining location in code. You have to perform the CPS transformation in full to try evaluating the resulting expression in Scheme interpreter. That expression will only contain lambdas and no continuations (in the sense of call/cc (1)).
The expression that you tried mixes them both - it defines done as simple lambda-defined function, but it is still used in the nested context as a continuation.
(1) (another source of confusion is calling the function arguments in the continuation-passing style "continuations". they are not "true" continuations; they are simple functions "to be called" in this or that eventuality, so colloquially they are also referred to as "continuations" although "contingencies" or even "handlers" could be better.)
See also another example of call/cc code translation.
Following that approach, translating your Scheme code into Common Lisp, we get:
;; (let/cc done
;; ((let/cc esc
;; (done (+ 1 (let/cc k
;; (esc k)))))
;; 3))
(prog (retval done arg1 func esc arg2 k arg3 arg4)
(setq done (lambda (x) (setq retval x) (go DONE))) ; 3
(setq arg1 3) ; 5
(setq esc (lambda (x) (setq func x) (go ESC))) ; 8
(setq arg3 1) ; 10
(setq k (lambda (x) (setq arg4 x) (go K))) ; 12
(setq arg4 (funcall esc k)) ; 13
K ; 11 continuation K
(setq arg2 (+ arg3 arg4)) ; 9
(setq func (funcall done arg2)) ; 7
ESC ; 6 continuation ESC
(setq retval (funcall func arg1)) ; 4
DONE ; 2 continuation DONE
(return retval)) ; 1
which indeed returns 4 (the lines of code are numbered in order as they are written, during the translation).
In the expression (call/cc (lambda (k) (k 12))), there are three continuations: (k 12), (lambda (k) (k 12)), and (call/cc (lambda (k) (k 12))). Which one is the "current continuation"?
And continuations in some books are viewed as a procedure which is waiting for a value and it will return immediately when it's applied to a value. Is that right?
Can anyone explain what current continuations are in detail?
Things like (k 12) are not continuations. There is a continuation associated with each subexpression in some larger program. So for example, the continuation of x in (* 3 (+ x 42)) is (lambda (_) (* 3 (+ _ 42))).
In your example, the "current continuation" of (call/cc (lambda (k) (k 12))) would be whatever is surrounding that expression. If you just typed it into a scheme prompt, there is nothing surrounding it, so the "current continuation" is simply (lambda (_) _). If you typed something like (* 3 (+ (call/cc (lambda (k) (k 12))) 42)), then the continuation is (lambda (_) (* 3 (+ _ 42))).
Note that the lambdas I used to represent the "current continuation" are not the same as what call/cc passes in (named k in your example). k has a special control effect of aborting the rest of the computation after evaluating the current continuation.
The continuation in this case is the "thing" that receives the return value of the call/cc invocation. Thus:
(display (call/cc (lambda (k) (k 12)))
has the same result as
(display 12)
Continuations in Scheme "look and feel" like procedures, but they do not actually behave like procedures. One thing that can help you understand continuations better is CPS transformations.
In CPS transformation, instead of a function returning a value, instead it takes a continuation parameter, and invokes the continuation with the result. So, a CPS-transformed sqrt function would be invoked with (sqrt 64 k) and rather than returning 8, it just invokes (k 8) in tail position.
Because continuations (in a CPS-transformed function) are tail-called, the function doesn't have to worry about the continuation returning, and in fact, in most cases, they are not expected to return.
With this in mind, here's a simple example of a function:
(define (hypot x y)
(sqrt (+ (* x x) (* y y))))
and its CPS-transformed version:
(define (hypot x y k)
(* x x (lambda (x2)
(* y y (lambda (y2)
(+ x2 y2 (lambda (sum)
(sqrt sum k))))))))
(assuming that *, +, and sqrt have all been CPS-transformed also, to accept a continuation argument).
So now, the interesting part: a CPS-transformed call/cc has the following definition:
(define (call/cc fn k)
(fn k k))
With CPS transformation, call/cc is easy to understand and easy to implement. Without CPS transformation, call/cc is likely to require a highly magical implementation (e.g., via stack copying, etc.).
The following example involves jumping into continuation and exiting out. Can somebody explain the flow of the function. I am moving in a circle around continuation, and do not know the entry and exit points of the function.
(define (prod-iterator lst)
(letrec ((return-result empty)
(resume-visit (lambda (dummy) (process-list lst 1)))
(process-list
(lambda (lst p)
(if (empty? lst)
(begin
(set! resume-visit (lambda (dummy) 0))
(return-result p))
(if (= 0 (first lst))
(begin
(call/cc ; Want to continue here after delivering result
(lambda (k)
(set! resume-visit k)
(return-result p)))
(process-list (rest lst) 1))
(process-list (rest lst) (* p (first lst))))))))
(lambda ()
(call/cc
(lambda (k)
(set! return-result k)
(resume-visit 'dummy))))))
(define iter (prod-iterator '(1 2 3 0 4 5 6 0 7 0 0 8 9)))
(iter) ; 6
(iter) ; 120
(iter) ; 7
(iter) ; 1
(iter) ; 72
(iter) ; 0
(iter) ; 0
Thanks.
The procedure iterates over a list, multiplying non-zero members and returning a result each time a zero is found. Resume-visit stores the continuation for processing the rest of the list, and return-result has the continuation of the call-site of the iterator. In the beginning, resume-visit is defined to process the entire list. Each time a zero is found, a continuation is captured, which when invoked executes (process-list (rest lst) 1) for whatever value lst had at the time. When the list is exhausted, resume-visit is set to a dummy procedure. Moreover, every time the program calls iter, it executes the following:
(call/cc
(lambda (k)
(set! return-result k)
(resume-visit 'dummy)))
That is, it captures the continuation of the caller, invoking it returns a value to the caller. The continuation is stored and the program jumps to process the rest of the list.
When the procedure calls resume-visit, the loop is entered, when return-result is called, the loop is exited.
If we want to examine process-list in more detail, let's assume the list is non-empty. Tho procedure employs basic recursion, accumulating a result until a zero is found. At that point, p is the accumulated value and lst is the list containing the zero. When we have a construction like (begin (call/cc (lambda (k) first)) rest), we first execute first expressions with k bound to a continuation. It is a procedure that when invoked, executes rest expressions. In this case, that continuation is stored and another continuation is invoked, which returns the accumulated result p to the caller of iter. That continuation will be invoked the next time iter is called, then the loop continues with the rest of the list. That is the point with the continuations, everything else is basic recursion.
What you need to keep in mind is that, a call to (call/cc f) will apply the function f passed as argument to call/cc to the current continuation. If that continuation is called with some argument a inside the function f, the execution will go to the corresponding call to call/cc, and the argument a will be returned as the return value of that call/cc.
Your program stores the continuation of "calling call/cc in iter" in the variable return-result, and begins processing the list. It multiplies the first 3 non-zero elements of the list before encountering the first 0. When it sees the 0, the continuation "processing the list element 0" is stored in resume-visit, and the value p is returned to the continuation return-result by calling (return-result p). This call will make the execution go back to the call/cc in iter, and that call/cc returns the passed value of p. So you see the first output 6.
The rest calls to iter are similar and will make the execution go back and forth between such two continuations. Manual analysis may be a little brain-twisting, you have to know what the execution context is when a continuation is restored.
You could achieve the same this way:
(define (prod-iter lst) (fold * 1 (remove zero? lst)))
... even though it could perform better by traversing only once.
For continuations, recall (pun intended) that all call/cc does is wait for "k" to be applied this way:
(call/cc (lambda (k) (k 'return-value)))
=> return-value
The trick here is that you can let call/cc return its own continuation so that it can be applied elsewhere after call/cc has returned like this:
;; returns twice; once to get bound to k, the other to return blah
(let ([k (call/cc (lambda (k) k))]) ;; k gets bound to a continuation
(k 'blah)) ;; k returns here
=> blah
This lets a continuation return more than once by saving it in a variable. Continuations simply return the value they are applied to.
Closures are functions that carry their environment variables along with them before arguments get bounded to them. They are ordinary lambdas.
Continuation-passing style is a way to pass closures as arguments to be applied later. We say that these closure arguments are continuations. Here's half of the current code from my sudoku generator/solver as an example demonstrating how continuation-passing style can simplify your algorithms:
#| the grid is internally represented as a vector of 81 numbers
example: (make-vector 81 0)
this builds a list of indexes |#
(define (cell n) (list (+ (* (car 9) (cadr n))))
(define (row n) (iota 9 (* n 9)))
(define (column n) (iota 9 n 9))
(define (region n)
(let* ([end (+ (* (floor-quotient n 3) 27)
(* (remainder n 3) 3))]
[i (+ end 21)])
(do ([i i
(- i (if (zero? (remainder i 3)) 7 1))]
[ls '() (cons (vector-ref *grid* i) ls)])
((= i end) ls))))
#| f is the continuation
usage examples:
(grid-ref *grid* row 0)
(grid-set! *grid* region 7) |#
(define (grid-ref g f n)
(map (lambda (i) (vector-ref g i)) (f n)))
(define (grid-set! g f n ls)
(for-each (lambda (i x) (vector-set! g i x))
(f n) ls))