Search code examples
schemecallcc

how does call/cc jump from two subrutine


I had written a scheme interpreter in scala, it really works, but I still can't figure out how does it switch between subroutines. for example:

(call/cc
 (lambda (k)
   (k 1)
   (display 2)))

I suppose the display procedure should be executed, but it doesn't.

In java, it just something likes this:

public class Test {

    static void call_cc(Consumer<Consumer> k,Consumer current){
        k.accept(current);
    }

    public static void main(String[] args){
        call_cc(consumer -> {
            consumer.accept(1);
            System.out.println(2);
        },System.out::print);
    }
}

so what's the problem with my opinion?


Solution

  • call/cc rewrites the program to continuation passing style. I've changed the code a little to display the result and have an additional code after so that you can get a hang of it, here is my modification:

    (display (call/cc
     (lambda (k)
       (k 1)
       (display 2))))
    (display "finished")
    

    In CPS call/cc is no more than this:

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

    The exit function, when used gets a closure representing the rest of the program in CPS, but its main function is to use it's own continuation instead of the supplied one.

    I define a CPS-version of display and the final continuation function halt

    ; CPS version of display
    (define (display& s k)
      (display s)
      (k 'undefined))
    
    ; The final continuation
    ; We make the REPL get the result and it display it
    (define halt values)
    

    Now for the rewrite of your code. CPS sort of changes the code to a step process that calculates just one thing at each step and while in Scheme the order of evaluation is up to the implementation in this form the order would be shown quite clearly. Notice that it never returns, just calls continuations in tail position:

    ; CPS version of the code
    (call/cc&
     (lambda (exit continuation)
       (exit 1 
             (lambda (unused)
               (display& 2 continuation))))
     (lambda (value)
       (display& value
                 (lambda (unused)
                   (display& "finished" halt)))))
    

    Now if you step through this it's obvious why it never displays "2".