I sort-of more-or-less have something more appealing w.r.t. compile-time/hygienic matching, but very non-portable:

First off, this version makes all state-variable names are declared - they cannot be made-up on the fly at runtime.

1) the state object is just an eq-hashtable.   A fresh state object is always empty.  Default values for state-variables are inserted as needed.

2) the implementation must provide a function (identifier->eqobj #'id) which returns a unique key associated with the identifier's binding (or non-binding).   Two identifiers produce the same eq? key IFF then they match hygienically, and are free-identifier=?.    These keys are the runtime keys used to store values in the state.   All state-variables are bound.

3) The original define-environment-monad form still works, but there is also (declare-fields <monad-name> <fieldspec> ...), where a field spec is either (fieldname) or (fieldname default).  This is how an extension module can declare more state variables (and not worry about symbolic collisions).

4) (fn (col) ...) does not bind 'col'.   col, and all declared state variables are initially defined as syntax-parameters.  (fn (col) ...) binds a temporary identifier and then parameterizes col to be identifier-syntax for the temporary.

5) (fn ((current-column col)) ...) does the simpler thing, just binds current-column

6) fn, with!, and with forms expand to a syntax-error if a state-variable name is not a declared state variable associated with the same monad that defined these syntax.  (This was ugly to implement, but it definitely helped with debugging).

So, the key non-portables here are identifier->eqobj and identifier-syntax.  Also to call identifier->eqobj with an identifier as an argument from syntax-rules macro output, (syntax id) -- aka #' -- is needed.

No need to pick this one apart.  It's not production-grade for sure.  Just though it might be interesting, and I've love to hear what others are thinking on this topic.


On Mon, Feb 11, 2019 at 7:48 AM Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:
Am Mo., 11. Feb. 2019 um 10:43 Uhr schrieb Alex Shinn <xxxxxx@gmail.com>:

[...]

> Without spending much time thinking about this originally I didn't consider it even possible in portable R7RS small, but I realize we could bind these to unique objects and key on those instead of the identifiers.
>
> If we really want this it would be a significant deviation, requiring changes to the spec and implementation, so would have to be a new SRFI.  I might take this up if there were serious interest from R7RS large.

[...]

I would volunteer to write a SRFI about the environment monad (which
may be of independent interest as well) and whose implementation can
then be used as a basis for SRFI 159 (thanks to the modular
implementation of SRFI 159 this is easy to achieve).

How are `local′ and `local!′ (or `with′ and `with!′, respectively)
supposed to interact? There are four possibilities:

(1) `local′ conceptually creates a new scope. All changes introduced
by `local!′ inside this scope are revoked when the `local′ scope is
left.
(2) Only changes by `local!′ to variables introduced by `local′ are
revoked when the `local′ scope is left.
(3) Changes by `local!′ always persist, even across `local′ scopes.
(4) It's up to the implementation.

(chibi monad environment) currently implements (2).

-- Marc
To unsubscribe from this list please go to http://www.simplelists.com/confirm.php?u=EvvNvm9ydcsHzVnadSGpgW6nii8cUCwx