sane@clitus.cs.uiuc.edu (Aamod Sane) (03/08/90)
Beginners question: I tried to understand the operation of call/cc from Dybvigs text and the R3.99 without success. Can someone give a good explanation here or a reference? It is said that the interepreter uses continuations which are not visible normally etc. but I dont quite get this. I would also like examples/references on the Continuation Passing style (just building of lambdas, not the call/cc variety). I know of examples such as gcd of a list where you can escape if a 1 is encountered without doing any computation, by building lambdas and using them only if 1 is not found. Thanks , Aamod Sane sane@cs.uiuc.edu
harlan@silver.ucs.indiana.edu (Pete Harlan) (03/10/90)
sane@cs.uiuc.edu (Aamod Sane) writes: | I tried to understand the operation of call/cc from Dybvigs text |and the R3.99 without success. Can someone give a good explanation here or |a reference? It is said that the interepreter uses continuations which are |not visible normally etc. but I dont quite get this. Chapters 16 and 17 of _Scheme and the Art of Programming_, by Springer/Friedman, McGraw-Hill/MIT Press, gives a thorough introduction to understanding call/cc. It includes many examples of its use, and many exercises for practicing. I highly recommend it. --- Pete Harlan harlan@silver.ucs.indiana.edu
bobg+@andrew.cmu.edu (Robert Steven Glickstein) (03/11/90)
When I was first trying to grok continuations as a Scheme novitiate, I found the sequence below very enlightening. Try to understand what's going on here; I'll post a detailed description in a day or two. > (define (identity-fn x) x) > (define (current-continuation) (call/cc identity-fn)) > (define foo (current-continuation)) > foo #[Continuation] > (foo 10) > foo 10 ______________ Bob Glickstein | Internet: bobg@andrew.cmu.edu Information Technology Center | Bitnet: bobg%andrew@cmuccvma.bitnet Carnegie Mellon University | UUCP: ...!harvard!andrew.cmu.edu!bobg Pittsburgh, PA 15213-3890 | (412) 268-6743 | Sinners can repent, but stupid is forever
schaut@cat9.cs.wisc.edu (Rick Schaut) (03/14/90)
In article <1990Mar8.064319.28399@brutus.cs.uiuc.edu> sane@clitus.cs.uiuc.edu (Aamod Sane) writes: | Beginners question: | | I tried to understand the operation of call/cc from Dybvigs text | and the R3.99 without success. Can someone give a good explanation here or | a reference? It is said that the interepreter uses continuations which are | not visible normally etc. but I dont quite get this. Most respondants have answered this portion, so I'll not add to the foray. | I would also like examples/references on the Continuation | Passing style (just building of lambdas, not the call/cc variety). | I know of examples such as gcd of a list where you can escape | if a 1 is encountered without doing any computation, by building lambdas | and using them only if 1 is not found. But, noone has answered this yet, so here goes. The following is a function that maps a function to a "tree" where a list represents a node in the tree. The top level list is the root node, and intermediate level lists are intermediate nodes (and are the children of the list which contains them), and any atoms are the leaves. For example, (1 (2 3) (4 (5 6)) 7) is one such tree. The catch is that the mapped function may return a value indicating that the value that was passed to it was not acceptable (for whatever reason). The tree-map function, then, should return a list of the form ('not-yet <unacceptable input value>). If the map was applied successfully, then the return sould be ('done <mapped tree>). The code that implements this is: (define (tree-map filter tree) (let ((result (tree-mapc filter tree (lambda(x)x)))) (if (and (not (null? result)) (list? result) (equal? 'not-yet (car result))) result (list 'done (revall result))))) (define (tree-mapc filter tree cont) (cond ((null? tree) (cont '())) ((atom? tree) (let ((result (filter tree))) (if (equal? result '*not-acceptable*) (list 'not-yet tree) result))) (else ; first, map the car of the tree using a fresh continuation ; i.e. build a continuation for the sublist (let ((result (tree-mapc filter (car tree) (lambda(x)x)))) (if (and (not (null? result)) (list? result) (equal? 'not-yet (car result))) result ;<- stop the recursion here, we've found an error ;else cons the result onto the current continuation and map the cdr (tree-mapc filter (cdr tree) (lambda(x)(cons result (cont x))))))))) (define (revall l) (cond ((atom? l) l) ((null? l) l) (else (let ((e (if (list? (car l)) (revall (car l)) (car l)))) (append (revall (cdr l)) (list e)))))) This is _very_ ugly code, and sufficient reason to understand and use call/cc. The equivalent code that uses call/cc is: (define (tree-map filter tree) (let ((result (call/cc (lambda(return)(tree-mapc filter tree return))))) (if (and (not (null? result)) (list? result) (equal? 'not-yet (car result))) result (list 'done result)))) (define (tree-mapc filter tree return) (cond ((null? tree) '()) ((atom? tree) (let ((result (filter tree))) (if (equal? result '*not-acceptable*) (tree-mapc filter (call/cc (lambda(context)(return (list 'not-yet tree context)))) return) result))) (else (cons (tree-mapc filter (car tree) return) (tree-mapc filter (cdr tree) return))))) Note also that this version uses call/cc to return control to the top-level tree-map function. This allows the calling routine to provide a substitute value back to tree-map, and tree-map will continue the original computation using the substituted value. As you mentioned, interpreters use this same construct when they encounter an error. They use a call/cc to invoke a debugging routine which, in turn, allows the user to correct the data/code. The corrections are then dropped back into the computation in which the error occurred and the computation is restarted where it left off. If your interpreter has a continue option in its break package, then it is using a call/cc in the error trap. -- Rick (schaut@garfield.cs.wisc.edu) "I'm a theory geek; we use Turing machines!"--Gary Lewandowski
dorai@hedera.rice.edu (Dorai Sitaram) (03/17/90)
In article <4459@daffy.cs.wisc.edu> schaut@cat9.cs.wisc.edu (Rick Schaut) writes: >But, noone has answered this yet, so here goes. The following is a function >that maps a function to a "tree" where a list represents a node in the >tree. The top level list is the root node, and intermediate level lists >are intermediate nodes (and are the children of the list which contains >them), and any atoms are the leaves. For example, (1 (2 3) (4 (5 6)) 7) >is one such tree. > >The catch is that the mapped function may return a value indicating that >the value that was passed to it was not acceptable (for whatever reason). >The tree-map function, then, should return a list of the form > > ('not-yet <unacceptable input value>). > >If the map was applied successfully, then the return sould be > > ('done <mapped tree>). > >The code that implements this is: > [cps-style code deleted] > >This is _very_ ugly code, and sufficient reason to understand and use >call/cc. The equivalent code that uses call/cc is: > >(define (tree-map filter tree) > (let ((result (call/cc (lambda(return)(tree-mapc filter tree return))))) > (if (and (not (null? result)) (list? result) (equal? 'not-yet (car result))) > result > (list 'done result)))) > >(define (tree-mapc filter tree return) > (cond > ((null? tree) '()) > ((atom? tree) > (let ((result (filter tree))) > (if (equal? result '*not-acceptable*) > (tree-mapc > filter > (call/cc (lambda(context)(return (list 'not-yet tree context)))) > return) > result))) > (else (cons > (tree-mapc filter (car tree) return) > (tree-mapc filter (cdr tree) return))))) > >Note also that this version uses call/cc to return control to the top-level >tree-map function. This allows the calling routine to provide a substitute >value back to tree-map, and tree-map will continue the original computation >using the substituted value. As you mentioned, interpreters use this same >construct when they encounter an error. They use a call/cc to invoke a >debugging routine which, in turn, allows the user to correct the data/code. >The corrections are then dropped back into the computation in which the >error occurred and the computation is restarted where it left off. If your >interpreter has a continue option in its break package, then it is using >a call/cc in the error trap. The above is OK, but schleps around too many arguments (filter, return) for no good reason. Tweaking a bit (and making do without the 'done tag): (define tree-map (lambda (filter tree) (call/cc (lambda (return) (let loop ([tree tree]) (cond [(null? tree) '()] [(atom? tree) (let ([result (filter tree)]) (if (eq? result 'not-acceptable) (call/cc (lambda (context) (return (list 'not-yet tree context)))) result))] [else (cons (loop (car tree)) (loop (cdr tree)))])))))) Ach, the above is _also_ unsatisfactory code: _two_ call/cc's; the first continuation-grab only contributes towards identifying the entry-point; the second grab doesn't need to be the whole continuation, just the difference between the entry and break points. The so-far best solution, based on one by Felleisen for a similar tree-walk problem in the paper "Theory and Practice of First-class Prompts", uses prompt (P) and the functional-continuation analog of call/cc (F). It certainly is more efficient and IMHO less sledgehammer-like than the call/cc version: (define tree-map (lambda (filter tree) (let loop ([tree tree]) (cond [(null? tree) '()] [(atom? tree) (let ([result (filter tree)]) (if (eq? result 'not-acceptable) (F (lambda (context) (list 'not-yet tree context)))))] [else (cons (loop (car tree)) (loop (cdr tree)))])))) A sample session: > (set! result (P (tree-map double-if-not-0 '(1 2 (3 0 (4 0 5)))))) (not-yet 0 #<procedure>) > (set! result (P ((caddr result) 'debug-insert))) (not-yet 0 #<procedure>) > (set! result (P ((caddr result) 'debug-insert-2))) (2 4 (6 debug-insert (8 debug-insert-2 10))) --dorai ps: (define double-if-not-0 (lambda (n) (if (zero? n) 'not-acceptable (* 2 n)))) ps2: For details about P and F, read the paper, or contact 'most anybody (once) from Indiana (except maybe the veep). Regular Scheme doesn't have them, though it should :-]. -- ------------------------------------------------------------------------------- It may be that the gulfs will wash us down; It may be we shall touch the Happy Isles. -------------------------------------------------------------------------------
dorai@hedera.rice.edu (Dorai Sitaram) (03/17/90)
In article <5832@brazos.Rice.edu> dorai@hedera.rice.edu, I perpetrate a typo:
$
$(define tree-map
$ (lambda (filter tree)
$ (let loop ([tree tree])
$ (cond [(null? tree) '()]
$ [(atom? tree)
$ (let ([result (filter tree)])
$ (if (eq? result 'not-acceptable)
$ (F (lambda (context)
$ (list 'not-yet tree context))))
result ;<-- was left out in the original
;^^^^^^
$ )]
$ [else (cons (loop (car tree))
$ (loop (cdr tree)))]))))
--dorai
--
-------------------------------------------------------------------------------
It may be that the gulfs will wash us down;
It may be we shall touch the Happy Isles.
-------------------------------------------------------------------------------
even@idt.unit.no (Even A. Karlsson) (06/30/90)
I have problem concerning call/cc. I want to pass some extra parameter along with the current continuation to a continuation, and I don't see how that is possible with call/cc. This is useful when modelling the coroutine aspect of classes in Simula where detach implicitly returns an environment (the class object) and a continuation (the rest of the body). Even-Andre Karlsson, IDT, NTH, NORWAY, even@idt.unit.no
raja@silver.ucs.indiana.edu (Raja Sooriamurthi) (06/30/90)
In comp.lang.scheme you write: >I have problem concerning call/cc. I want to pass some extra parameter >along with the current continuation to a continuation, and I don't see >how that is possible with call/cc. This is useful when modelling >the coroutine aspect of classes in Simula where detach implicitly >returns an environment (the class object) and a continuation (the rest >of the body). I don't know if I understand your question clearly, but will the below outline solve your problem? (define receiver (lambda (x) (lambda (current-cont) . body .))) (call/cc (receiver extra-param)) - Raja raja@silver.ucs.indiana.edu
krulwich@ils.nwu.edu (Bruce &) (07/03/90)
In article <1990Jun30.113103.5377@idt.unit.no>, even@idt (Even A. Karlsson) writes: >I have problem concerning call/cc. I want to pass some extra parameter >along with the current continuation to a continuation, and I don't see how >that is possible with call/cc. This is useful when modelling the coroutine >aspect of classes in Simula where detach implicitly returns an environment >(the class object) and a continuation (the rest of the body). Can you be more specific? It seems to me that you should be able to do this by lexically capturing other variables in the closure you give to CALL/CC. Can you give an example of the behavior you want? Bruce Krulwich krulwich@ils.nwu.edu