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/ccis this:Looking at your first code I think it becomes something like this:
Here is whats happening:
xandfcall/cc&callsfxis set tor(continuation a)xwith "30"xwith "30" 3 lines up and continueSo 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 callsrand passes the continuation but it gets circumvented tocall/ccoriginal 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/ccto work it needs to do it until the next continuation prompt.It's probably better to look at
call/ccwithout mutation in the beginning. eg.Now this turns into:
Now we know
exitgets called and thus this whole thing turns into: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.