How to use continuations in scheme?

淺唱寂寞╮ 提交于 2020-01-05 04:56:10

问题


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.


回答1:


Does you lisp implementation transform user code to continuation passing style? In that case it is easy peasy. call/cc is this:

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

Looking at your first code I think it becomes something like this:

((lambda (x k)
   ((lambda (f k)
      (call/cc& f (lambda (v) ; continuation a
                    (display& v (lambda (_) ; continuation b
                                  (x "30" k))))))
    (lambda (r k)
      (set!& x r (lambda (_) ; continuation c
                   (r 20 (lambda (v) ; continuation d
                           (display& v (lambda (_) ; continuation e
                                         (display& "10" k))))))))
    k)
   0
   halt)

Here is whats happening:

  • We make x and f
  • call/cc& calls f
  • x is set to r (continuation a)
  • r gets called with 20 as value
  • continuation c is ignore, instead continuation a is called with 20
  • 20 gets displayed, then continuation b gets called
  • b calls x with "30"
  • continuation k is ignored, instead continuation a is called with 30
  • 30 gets displayed, then continuation b gets called
  • go to "b calls x with "30" 3 lines up and continue

So 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 calls r and passes the continuation but it gets circumvented to call/cc original 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/cc to work it needs to do it until the next continuation prompt.

It's probably better to look at call/cc without mutation in the beginning. eg.

(+ 2 (call/cc (lambda (exit)
                (+ 3 (* 5 (exit 11))))))

Now this turns into:

(call/cc& (lambda (exit k)
            (exit 11 (lambda (v)
                       (*& 5 v (lambda (v)
                                 (+& 3 v k))))))
          (lambda (v)
            (+& 2 v repl-display)))

Now we know exit gets called and thus this whole thing turns into:

((lambda (v) (+& 2 v repl-display)) 11)

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.



来源:https://stackoverflow.com/questions/55788192/how-to-use-continuations-in-scheme

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!