> > 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