At Sun, 25 Jul 2021 10:35:00 -0700 (PDT), Greg Rosenblatt wrote:
> I was surprised that subsequent re-entries can observe modifications from
> the earlier ones, since my mental model of dynamic parameters was that
> their values were retrieved from a fresh dynamic calling context, whose
> frames are copied each time the delimited continuation is invoked.
> [...]
> I'm also interested in the reasons behind the current design. Is there a
> downside to storing parameter bindings directly on the stack, rather than
> in a thread cell pointed to by the stack?
I think I see what you mean here, but I also think that idea would run
into trouble. A (delimited) continuation is invoked every time you
return a value to a continuation, whether or not that continuation was
captured by `call/cc`. Internally, in fact, the current continuation
frequently gets reified and invoked through returns. I think it would
create a bad interaction among features if a distinction were made
between explicit (applying a captured continuation) and implicit
(returning a value) continuation invocations.
> I'd like to be able to resume that continuation in the same context
> multiple times without observing modifications caused by other
> resumptions. [...] But it looks like dynamic parameters actually rely
> on a shared mutable cell, in this case a thread cell.
Yes, parameters are based on a mapping
parameter * thread * paramterizization -> box
When you set or look up a parameter value, the thread part comes from
`current-thread`, and the parameterization part comes from a
continuation mark. So, that's why capture and restore within a thread
sees the same box, but capture and restore of a continuation in a
different thread gets a different box.
I think you want to introduce a notion of "resume" that replaces the
thread part of the mapping, plus a replacement notion of
parameterization. That is, each time you resume, the applied
continuation should extend one that maps an internal mark to the
current resume. A replacement for `parameterize` would also install a
mark (with a different key) to identify a "parameterization", while
also mapping the parameter * parameterization * resume to a fresh box.
When you look up a parameter value or mutate a parameter, the current
mark for the resume and the current mark for the parameterization would
let you find the right box.
It may be best to have a "parameterization" specific to each
"parameter" (i.e., a separate mark for each parameter), instead of
aggregating them in the way `parameterize` does. In the case of threads
and parameters, aggregation makes it easier to create a new thread that
inherits the parameterization of the creating thread. But if you don't
need that, aggregation also works less well with delimited continuation
capture, because `parameterize` captures all parameter values and not
just the ones that are set in the `parameterize` form.