1

I would like to understand how to implement coroutines without call/cc.

I began with this little example to understand how to modify a code so that I can use it without call/cc:

(define x 0)

( + 2 (call-with-current-continuation
  (lambda (cont)
    (set! x cont)
  3)))

(x 4)

When I execute the function with the call/cc it gives me 5, then 6 when I execute (x 4).

I use this function to replace the call/cc :

(define (call/cc-cps f continuation)
   (f continuation continuation))

I tried to change this function into CPS (continuation-passing style):

(call/cc-cps
  (lambda (exit cont)
   (set! x cont)
   3)
(lambda (value)
  (+ value 2)))

But when I execute it, instead of having 5 I get 3. But I do get 6 with (x 4).

Can you tell me what is wrong ? thank you

Will Ness
  • 70,110
  • 9
  • 98
  • 181

1 Answers1

1

With CPS you don't have top level continuations prompts so for a comparison you need to put you code in an empty let like this:

(let ()
  (define x 0)
  (+ 2 (call-with-current-continuation
         (lambda (cont)
           (set! x cont)
           3))) ; ==> 5
  (x 4))
  ; ==> 6
  ; ==> 6
  ; ==> 6
  ; ... forever 

This becomes an infinite loop since you call (x 4) after the summing every time.

;; CPS-version of +
(define (+& a b continuation)
  (continuation (+ a b)))

Notice the &. It's used to indicate that the function takes one extra argument, the continuation. In CPS you define call/cc like this:

(define (call/cc& f& continuation)
  (define (exit& value actual-continuation)
    (continuation value))
  (f& exit& continuation))

Notice it's quite different from your version. It passes the exit function to f& and instead of using the passed actual-continuation that would do all the remaining computation it passes it to the continuation of call/cc& instead.

Now we can rewrite your program into this:

((lambda (x& halt-continuation) 
  (call/cc& (lambda (cont continuation)
              ((lambda (ingnored-undefined-value) (continuation 3))
               (set! x& cont)))
            (lambda (three)
              (+& 2 three (lambda (ignored-result)
                      (x& 4 halt-continuation))))))
 0 values)

ignored-result is 5 the first time and the infinite other times it calculates it with 4 ignored-result is 6, just like in the non CPS version with let.

I use DrRacket which has a pretty darn good debugger and you can step and see exactly what is happening. I added a break point at the +& call and pressed |> and saw the variable three fist be 3 then 4 infinite times.

CPS isn't easy. call/cc gives us the benefits of CPS without making the code much harder to read. Implementing coroutines without call/cc would be challenging for me to write as I though it was quite complex to do it with call/cc to begin with, especially since you have a infinite loop if you are not very careful.

Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • Thank you for the response. What I want to do is to stock the continuation in the x variable that is define to 0 at the beginning so that when I use it, it just do a + 2 as in the example. But in your code x still is 0. How can I change that ? – user1241025 Aug 19 '17 at 07:53
  • @user1241025 `x&` does change since i `set!` it, just as in your original code. Or else you would get an apply error. – Sylwester Aug 19 '17 at 15:21