views:

67

answers:

1

I am having some trouble understanding the behavior of the following Scheme program:

(define c
  (dynamic-wind
    (lambda () (display 'IN)(newline))
    (lambda () (call/cc (lambda (k)
                     (display 'X)(newline)
                     k)))
    (lambda () (display 'OUT)(newline))))

As I understand, c will be bound to the continution created right before "(display 'X)".

But using c seems to modify itself! The define above prints (as I expected) IN, X and OUT:

IN
X
OUT

And it is a procedure:

#;2> c
#<procedure (a9869 . results1678)>

Now, I would expect that when it is called again, X would be printed, and it is not!

#;3> (c)
IN
OUT

And now c is not a procedure anymore, and a second invokation of c won't work!

#;4> c    ;; the REPL doesn't answer this, so there are no values returned
#;5> (c)

Error: call of non-procedure: #<unspecified>

        Call history:

        <syntax>            (c)
        <eval>              (c)    <--

I was expecting that each invokation to (c) would do the same thing -- print IN, X, and OUT. What am I missing?

+7  A: 

Running this in Racket is a little more helpful:

-> (define c
     (dynamic-wind
       (lambda () (display 'IN)(newline))
       (lambda () (call/cc (lambda (k)
                             (display 'X)(newline)
                             k)))
       (lambda () (display 'OUT)(newline))))
IN
X
OUT
-> c
#<continuation>
-> (c)
IN
OUT
define-values: context (defining "c") expected 1 value, received 0 values
-> (c 99)
IN
OUT
-> c
99

Note specifically that c gets bound to a continuation value -- because your expression returns k as the value. And k itself is the continuation of the value expression, which means that this continuation is the one waiting to get a value to be bound to c. So invoking it requires a single value, as Racket requires, which helps clarifying what happens here (MIT Scheme seems to silently treat it as unspecified). Anyway, applying this continuation on 99 means that the return value from that expression is 99 -- so you jump back into the context (printing IN) and return 99 to be bound to c, and print OUT on your way out. You've now modified c to have that 99 instead, so you can't call it a third time.

Eli Barzilay
Oh, I see! Now I feel like it was a really silly question after all. Thanks a lot!
josh
@Eli: "so you can't call it a third time" -- unless of course you always call (c c), which binds c to the continuation of a procedure defining c... :-)
Jay
@Jay: you're right, of course. I was focusing on josh's setup, except that Racket requires some value to be sent -- so using `c` would have made it more confusing.
Eli Barzilay
Hey, now this is funny... I defined c as above and then called (c) five times on MzScheme v4.2.4 and it just worked. (?!)It fails on Chicken, Guile, Gambit, Ypsilon and SISC.
Jay
You mean `(c)` with no arguments? It should have failed in 4.2.4 too.
Eli Barzilay
Yes -- (c) with no arguments...
josh