[plt-scheme] composable continuations and dynamic state
In the following test of the interaction between dynamic state and
composable continuations, I observed a difference between dynamic
bindings established through parameters and PARAMETRIZE, and dynamic
bindings established through DYNAMIC-WIND. With DYNAMIC-WIND the test
behaves as I expect; with PARAMETRIZE it does not.
(define (test make-parameter parameter-value with-parametrization)
(let ((a (make-parameter 'initial-a))
(b (make-parameter 'initial-b))
(prompt-tag (make-continuation-prompt-tag)))
(define (show n)
(write `(,n ,(parameter-value a) ,(parameter-value b)))
(newline))
(let ((f
(with-parametrization a 'a0
(lambda ()
(call-with-continuation-prompt
(lambda ()
(show 1)
(with-parametrization b 'b0
(lambda ()
(show (call-with-composable-continuation
(lambda (continuation)
(show 2)
(abort-current-continuation prompt-tag
(lambda ()
(show 3)
continuation)))
prompt-tag)))))
prompt-tag)))))
(show 4)
(f 5)
(with-parametrization a 'a1
(lambda ()
(f 6)))
(with-parametrization b 'b1
(lambda ()
(f 7))))))
Below are the implementations of the arguments MAKE-PARAMETER,
PARAMETER-VALUE, and WITH-PARAMETRIZATION, but first, a summary of
results:
parametrize dynamic-wind
(1 a0 initial-b) (1 a0 initial-b)
(2 a0 b0) (2 a0 b0)
(3 a0 initial-b) (3 a0 initial-b)
(4 initial-a initial-b) (4 initial-a initial-b)
(5 a0 b0) (5 initial-a b0)
(6 a0 b0) (6 a1 b0)
(7 a0 b0) (7 initial-a b0)
The following definitions are all obvious. Both naive and sensible
DYNAMIC-WIND implementations yield identical output. I am aware that
there is an extra layer of indirection in the use of parameters, since
continuation marks map parameters to thread cells, which threads map
to values, and PARAMETRIZE only binds the parameters to fresh thread
cells in the continuation marks. However, since there is only dynamic
binding and no mutation in the above TEST procedure, I think that
every thread cell in question will only ever have a single value.
(This is the same reason for why the naive and sensible DYNAMIC-WIND
implementations yield identical output.)
(define (make-parameter/parametrize initial-value)
(make-parameter initial-value))
(define (parameter-value/parametrize parameter)
(parameter))
(define (with-parametrization/parametrize parameter value thunk)
(parameterize ((parameter value))
(thunk)))
(define (test/parametrize)
(test make-parameter/parametrize
parameter-value/parametrize
with-parametrization/parametrize))
(define (make-parameter/dynamic-wind initial-value)
(box initial-value))
(define (parameter-value/dynamic-wind parameter)
(unbox parameter))
(define (with-parametrization/dynamic-wind-naive parameter value thunk)
(let ((original (unbox parameter)))
(dynamic-wind
(lambda ()
(set-box! parameter value))
thunk
(lambda ()
(set-box! parameter original)))))
(define (with-parametrization/dynamic-wind-sensible parameter value thunk)
(let ((cache #f))
(dynamic-wind
(lambda ()
(set! cache (unbox parameter))
(set-box! parameter value)
(set! value #f))
thunk
(lambda ()
(set! value (unbox parameter))
(set-box! parameter cache)
(set! cache #f)))))
(define (test/dynamic-wind-naive)
(test make-parameter/dynamic-wind
parameter-value/dynamic-wind
with-parametrization/dynamic-wind-naive))
(define (test/dynamic-wind-sensible)
(test make-parameter/dynamic-wind
parameter-value/dynamic-wind
with-parametrization/dynamic-wind-sensible))