I have the following Scheme program:
(define sg-pair (let ()
(define x #f)
(cons
(call/cc (lambda (c)
(set! x c)))
x)))
(display sg-pair)
(display "\n")
(if (eq? (car sg-pair) (void))
(let ()
((cdr sg-pair) "first")))
(if (string? (car sg-pair))
(let ()
((cdr sg-pair) 2)))
This prints the following:
(#!void . #<procedure #2>)
(first . #<procedure #2>)
(2 . #<procedure #2>)
My current understanding of how this pair would be constructed is:
c
) is created upon invocation of call/cc
. At this time, x
is #f
, and has yet to be evaluated as the second argument of cons
.lambda
passed to call/cc
, x
is set to a non-false (continuation) value.call/cc
returns naturally, and returns nothing (#!void
), and x
is retrieved from the prior set call.cdr
, I would expect for the map of x -> #f
, known when the continuation was created, to be restored. Then, because the lambda
body of call/cc
is not run a second time, x
would remain false.x
would be evaluated after the call/cc
, this time to #f
, resulting in the second display
's cdr
being different, and the second call of the cdr
failing.However, what really happens is that the prior value of x
is remembered when the continuation is re-entered. Why, and how? What mechanism does Scheme actually use to remember environments?
"I would expect for the map of
x -> #f
, known when the continuation was created, to be restored"
No. Nothing is being restored. x
is a binding, local to that let
form. It has value. Whatever value it has, is its value. No prior values are remembered, only its current value is maintained, sitting in that place in memory to which the binding points.
Whatever the state of memory is, is not changed. Continuations are only about control flow.
We re-enter same point in code, under same (lexical) bindings. But this does not affect the values those bindings hold. We just get access to those bindings, and - thus - their values, whatever these bindings' values are at the moment.
Calling the continuation ((cdr sg-pair) value)
is equivalent to
(set! sg-pair (cons value x))
but x
is the same hidden local binding x
. Its value has not changed. It was stored in (cdr sg-pair)
. Thus each call ((cdr sg-pair) value)
is equivalent to
(set! sg-pair (cons value (cdr sg-pair)))
NB. As Shawn points out in a comment, this code only works as intended provided (cons A B)
evaluates A
before B
.