Re: Some comments Marc Feeley 30 Dec 2002 14:29 UTC

> > When writing code, you really have to think of the parameters created
> > by make-parameter as read-only, and those created by
> > make-mutable-parameter as read-write.  It is only to simplify the
> > migration of legacy code that the parameters created by make-parameter
> > are possibly mutable (with an implementation dependent semantics).
>
> This is unfortunate, since this SRFI really moves all that "legacy"
> code into non-specifiedness...

Since SRFI 39 did not exist when the legacy code was written the code
never conformed to it.  So SRFI 39 does not "move" the code into
non-specifiedness.  The other choice (of having "make-parameter"
create the mutable parameters as specified in SRFI 39) is even less
appealing because in that case PLT legacy code would no longer work if
PLT adopted that semantics.

The existence of two kinds of parameters is mainly motivated by
compatibility with PLT (so that there is very little work needed to
implement it in PLT).

> I think parameters (as used in Chez, PLT or (say) Chicken) are
> (IMHO) well understood and heavily used.

For these systems and Gambit-C, "make-mutable-parameter" and
"make-parameter" can be defined as the native "make-parameter" when
multithreading is not used.  The semantic differences only matter with
PLT when threads are being used and the parameters are mutated (indeed
only PLT and Chicken implement threads and the Chicken manual does not
explain the interaction between parameters and threads).  I view SRFI
39's main contribution as a specification of how mutation of
parameters should work in the presence of threads.

> >>2) `parameterize' changes the parameters by "swapping", as
> >>    in the traditional implementation.
> >
> >
> > What do you mean by "swapping"?  Do you mean that parameterize does
> > not create a new cell and simply saves the content of the cell,
> > changes the content of the cell for the duration of the body, and then
> > restores the cell?
>
> Right.
>
>  > This would be unbelievably difficult to use in a
> > multithreaded system, unless of course thread creation copies the
> > dynamic environment of the parent (which I guess is why you want point
> > 3).  Moreover, this implementation is expensive when using
> > continuations to jump between different dynamic scopes (you have to
> > undo a bunch of parameter mutations, and then redo a bunch of other
> > mutations).
>
> I don't think so. You could use a "copy-on-write" strategy that
> copies the dynamic (parameter) environment only if a thread actually
> mutates the parameter: say you have a global parameter environment,
> and `make-parameter' puts the value into this. Every thread gets its
> own parameter-environment (initially a copy of the parent thread's env [*]).
> Then, when the parameter is referenced, you check wether the thread's
> parameter-environment contains a value, and, if not, take the
> global one. Mutating a parameter does the copying (if needed).
> This should result in a negligible overhead for thread-creation,
> and a not-too-expensive overhead in the worst case (a shallow vector
> copy is used in Chicken, for example).

No "copy-on-write" is not a valid implementation.  The reason is that
the "swapping" semantics requires the child thread to have an
independent copy of the parent's thread.  So the child must get a
snapshot of the parent's dynamic environment which will make the
child's mutations invisible to the parent ***AND*** the parent's
mutations invisible to the child.  The copy-on-write approach you
suggest only makes the child's mutations invisible to the parent.

> >>3) Make all parameters thread-local, child threads inherit
> >>    the parameter-settings of their parents.
> >
> >
> > By thread-local you mean copy the dynamic environment (including the
> > cells)?  Then see my previous response to a comment by Matthew Flatt
> > (to summarize: it isn't clean and it is expensive when you create a
> > thread).
> >
>
> Clean? Well, that's a matter of taste. Expensive? Not necessarily
> (see above).

When I say "clean" I don't mean it so subjectively.  A strong argument
can be made that the semantics I propose in SRFI 39 for dynamic
binding are closer to the lexical binding semantics.  What does this
mean?  Well, if you look at how environments are manipulated in the
denotational semantics (section 7.2 of R5RS) you will see that there
are only two operations on environments "lookup" and "extends".
"lookup" returns the location in the store that is bound to an
identifier.  The value associated to a location in the store is
obtained with "hold" and "assign" changes the value associated with
the location.  Dynamic binding as I propose it uses exactly the same
operations (with the minor point that the domain of "lookup" and
"extends" must be changed to accept parameter objects).  In addition
to the lexical environment, the semantic functions also need an extra
environment: the dynamic environment.  The dynamic binding semantics
is simply obtained by having procedure call pass this dynamic
environment to the called procedure (and this is the only thing that
distinguished it from lexical binding).  Similarly (but obviously
outside the scope of R5RS), the creation of a new thread would capture
the dynamic environment of the parent thread.  That's it.  There is no
need for an additional semantic operation for copying dynamic
environments (which is non trivial because it must allocate store).
Dynamic binding is a simple and natural extension of the R5RS
semantics.  This is what I mean by "clean".

Marc