This is the mail archive of the guile@cygnus.com mailing list for the guile project.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
Firstly, the idea of call/cc is simple when you get it right. The usual problem is that people don't know exactly what a continuation is. Here's an attempt at a quick explanation: Continuations ------------- A continuation is a property of an expression E inside a containing expression P: P = (... (... E ...) ...) It is a procedure which represents everything that will happen from the point in execution time when E returns its value. It takes the value that would normally be supplied by E as its single argument and then proceeds as if the value had been supplied by E. It is often correct to say that the continuation of E above is: (lambda (x) (... (... x ...) ...)) I say "often" because the continuation, in fact, also captures the things that happened around P, for example the exit to the repl loop. call/cc ------- `call/cc' takes a "receiver" as argument. (A receiver is simply a procedure which takes a continuation as argument.) `call/cc' captures it's own continuation and passes it to the receiver, i.e., `call/cc' applies the receiver to its own continuation. This means that if, as a consequence of the evaluation of the receiver body, at any later time the continuation is called with an argument x, evaluation will continue as if x was returned by the `call/cc' expression. In fact, this is what will happen. A strange thing about this is that if we call the continuation multiple times, the `call/cc' expression will return multiple times. Example ------- (define set-counter! #f) ;; WARNING There's a dog burried in this code (define counter (let ((c (call/cc (lambda (continuation) (set! set-counter! continuation) 0)))) (lambda () (set! c (+ 1 c)) c))) Now let's see how this code behaves > (counter) 1 > (counter) 2 > (set-counter! 0) > (counter) 1 Everything seems OK, right? BUT! > (define (counter) 'foo) > (set-counter! 0) > (counter) 1 !!! We had redefined counter to return `foo'. What has happened? Answer: `call/cc' captured it's continuation, i.e. everything that will happen when `call/cc' returns. This includes the definition of `counter', which means that each call to `set-counter!' will redefine `counter'. This shows that some care is needed when using call/cc. To avoid unexpected side-effects when using `call/cc': Be very strict when using `call/cc'. Always be aware that calling a continuation is essentially the same thing as a "jump" into a previous state of evaluation. Also note that that state of evaluation doesn't include the values of variables. Here are two suggestions on how to handle your specific problem: ;;; Dahl-Hoare Korutiner ;;; ;;; Described by ;;; ;;; Christopher T. Haynes och Daniel P. Friedman ;;; ;;; "Continuations and Coroutines", 1984 ;;; ;;; Modified to take arbitrary number of arguments ;;; by Joacim Halén 950410 ;;; (define call/cc call-with-current-continuation) (define make-coroutine (lambda (f) (call/cc (lambda (maker) (let ((lcs '*) (cc '*)) (let ((resume (lambda (dest . args) (call/cc (lambda (k) (set! lcs k) (apply dest (cons cc args)))))) (detach (lambda args (call/cc (lambda (k) (set! lcs k) (apply cc args)))))) (detach ((f resume detach) (call/cc (lambda (k) (set! lcs k) (maker (lambda (cont . args) (set! cc cont) (apply lcs args))))))))))))) (define call (lambda (dest . args) (call/cc (lambda (k) (apply dest (cons k args)))))) ;;; Version with one coroutine (define scopeWalkDemo (let ((coroutine #f)) (lambda (beginp) (if beginp (set! coroutine (make-coroutine (lambda (resume detach) (lambda (init) (let ((l (list 'cow 'horse 'pig ))) (do ((i 0 (+ i 1))) ((= i (length l))) (detach (list-ref l i))))))))) (call coroutine #f)))) (define v-demo (make-vector 3)) (define (test) (do ((j 0 (1+ j)) (cont #t) (tmp #f)) ((>= j 3)) (write-line (list cont j v-demo)) (set! tmp (scopeWalkDemo cont)) (vector-set! v-demo j tmp) (set! cont #f))) ;;; Version with two cooperating coroutines (define (scopeWalkDemo2) (make-coroutine (lambda (resume detach) (lambda (caller) (detach) (let ((l (list 'cow 'horse 'pig ))) (do ((i 0 (+ i 1))) ((= i (length l))) (resume caller (list-ref l i)))))))) (define (test2) (let* ((walker (scopeWalkDemo2)) (user (make-coroutine (lambda (resume detach) (lambda (init) (do ((j 0 (1+ j)) (cont #t) (tmp #f)) ((>= j 3)) (write-line (list cont j v-demo)) (set! tmp (resume walker #f)) (vector-set! v-demo j tmp) (set! cont #f))))))) (call walker user) ; Pass caller (call user #f)))