As the one who brought up the issue, I feel obliged to come up with a suggestion to fix, but haven't quite got it yet. We should discuss the fundamental semantic difference between functional and linear-updating versions in the "Linear update" section, and introduce the conversion/copy procedures. We may give examples of different implementation strategies (e.g. all-functional, flagging the object, etc.).
On Sun, Jun 6, 2021 at 7:36 AM Marc Nieper-Wißkirchen < [email protected]> wrote: > John, I am not sure whether your analysis hits the nail right on its head. > > When we have been talking about persistent vs. transient objects here, we > haven't meant that this has to be reflected on the Scheme language level. > In fact, we have been talking about these on the same semantic level as > SRFI 113 and SRFI 146 already do. > > The root cause of our discussion here is about a different thing, that has > been surfaced thanks to Shiro: With the current semantics, we have it that > if any of the provided linear update procedures exported by SRFI 113 or > SRFI 146 do, in fact, modifier its argument, every functional update > procedure is forced to return a newly allocated structure. This makes > implementations whose linear update procedures are more than just wrappers > around the functional ones very inefficient when it comes to the functional > interface. The current semantics of SRFI 113 or SRFI 146 is just bad; it > connects two very different things that should have been considered > separately. > > The solution that is proposed by us here is to untangle the dependency of > the semantics of the functional on the implementation-aspect of the > linear-update part of the specs. Our solution means to drop the requirement > that functional update procedures return newly allocated objects. Instead, > it will be made an error to feed an object returned by a functional update > procedure into a linear update procedure without copying it first. > > When we want to model this in a sufficiently rich static typing system > (which would be outside the Scheme language), it makes sense to talk about > transient and persitent versions of the objects. We can optionally get some > type-safety in Scheme at the runtime level by splitting the copy procedure > into three versions: > > transient->persistent > persistent->transient > copy-transient > > An implementation could add a flag to each object to remember whether it > was produced by a functional update procedure and check this flag when the > above procedures (whose names are just placeholders) are called. > > In an implementation that does not want to check the requirements at > runtime, transient->persistent would be the identity, and the other two the > same copying procedure. > > In a nutshell, the current requirement that newly allocated structures are > returned will be dropped in exchange for a single procedure that actually > does have to newly allocate, namely persistent->transient. > > -- Marc > > Am So., 6. Juni 2021 um 18:56 Uhr schrieb John Cowan <[email protected]>: > >> >> >> On Sat, Jun 5, 2021 at 8:05 AM Marc Nieper-Wißkirchen < >> [email protected]> wrote: >> >> Wolfgang, what do you think? We should get it right with SRFI 224 first >>> (before it is finalized) and then we can correct SRFI 113 and 146 (am I >>> missing another relevant SRFI) ex post facto. >>> >> >> I haven't been following this thread closely, but it seems to me that the >> topic has drifted from functional vs. linear-update *procedures* to >> persistent vs. transient *objects*. Invoking (foo-swizzle bar) guarantees >> that bar will not be visibly mutated, whereas invoking (foo-swizzle! bar) >> makes no such guarantee. If foo objects are not in fact mutable, these >> are probably the same procedure. If foos are in fact mutable, then >> foo-swizzle probably makes a copy and foo-swizzle! does not. >> >> Only in the case where foos can be either immutable or mutable is it >> necessary to have two types and keep track of what you've got, and provide >> foo-freeze and foo-thaw to convert between them And there is an >> alternative to this, namely the Builder pattern, in which foos and >> foo-builders have entirely separate APIs: foos are immutable, whereas >> foo-builders are not, but only support mutation operations and cannot be >> used as foos. >> >> We have generally avoided the Builder pattern in favor of the Constructor >> pattern, but there are cases (histograms are my favorite one) where >> mutability is highly desirable, and the Builder pattern might be a win. >> >
