Thanks, I think that makes sense.
Is the `parameter * thread * parameterization -> box` part implemented as
something like a global weak-hash, or is it built directly into the stack
representation?
Continuing the other conversation for a moment with a code reference to
explain to my future self, or anyone else curious, what I meant by
"algebraic state effects". Here's a simple implementation with a couple
examples:
```
#lang racket/base
(require racket/control)
(struct state-parameter (tag)
#:property prop:procedure
(case-lambda
((self) (shift-at (state-parameter-tag self) k (lambda (x) ((k
x) x))))
((self v) (shift-at (state-parameter-tag self) k (lambda (_) ((k
(void)) v))))))
;; Analogous to make-parameter, but without a default value
(define (make-state-parameter)
(state-parameter (make-continuation-prompt-tag)))
(define-syntax state-parameterize*
(syntax-rules ()
((_ () body ...)
(let () body ...))
((_ ((e.p e)) body ...)
(let ((p e.p) (x e))
((reset-at (state-parameter-tag p)
(let ((result (begin body ...)))
(lambda (_) result)))
x)))
((_ (b bs ...) body ...)
(state-parameterize* (b) (state-parameterize* (bs ...) body ...)))))
(define-syntax state-parameterize-etc
(syntax-rules ()
((_ final () body ...)
(state-parameterize* final body ...))
((_ (final ...) ((e.p e) bs ...) body ...)
(let ((p e.p) (x e))
(state-parameterize-etc (final ... (p x)) (bs ...) body ...)))))
;; Analogous to parameterize
(define-syntax-rule (state-parameterize etc ...) (state-parameterize-etc ()
etc ...))
;; The same kind of single state example
(define P (make-state-parameter))
(define (show) (displayln (P)))
(define (inc) (P (+ 1 (P))))
(define K
(reset
(state-parameterize ((P 0))
(show)
(inc)
(shift k k)
(show)
(inc)
(P))))
(displayln "with algebraic state:")
(K) ; 2
(K) ; 2
(K) ; 2
;; An example with multiple states
(define R (make-state-parameter))
(define Q (make-state-parameter))
(define (show2) (displayln `(R: ,(R) Q: ,(Q))))
(define (inc2) (R (+ 1 (R))) (Q (- (Q) 1)))
(define J
(reset
(state-parameterize ((R 0) (Q 0))
(show2)
(inc2)
(shift k k)
(show2)
(inc2)
(list (R) (Q)))))
(displayln "with multiple algebraic states:")
(J) ; 2 -2
(J) ; 2 -2
(J) ; 2 -2
```
On Friday, July 30, 2021 at 11:04:09 AM UTC-4 Matthew Flatt wrote:
> 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.
>
--
You received this message because you are subscribed to the Google Groups
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/racket-users/1d037d3f-83e6-45b2-b215-ea574ed586dcn%40googlegroups.com.