Yes, I agree to both points. It's up to Arthur if a PFN is appropriate: I"m in favor of having one. Can you draft it? (If Arthur is not okay with it, it will go to Committee C.)
On Sat, Feb 11, 2023 at 7:05 PM Shiro Kawai <[email protected]> wrote: > Good point. > > It is certainly undesirable that merely calling lseq-for-each forces all > elements in the argument, and I'm for reimplementing it. > > Regarding the order of effects, I have an opinion. The design choice of > lseq is to loosen the strict laziness to get efficiency. Srfi-127 is > inspired by Gauche's lseq, which in turn is inspired by Clojure's seq. I > believe Clojure may realize seq's elements in a chunk, e.g. 32 elements at > a time, at least in some situations. So you shouldn't rely on the timing > of evaluation anyway; all that's guaranteed is that the data is available > by the time you accessed it, and the whole thing runs in a bounded space if > you don't keep the previous elements. I think lseq should be used in the > same way, that is, you shouldn't rely on that (lseq-for-each write-char > chars) prints "read write read write ..."; it may print "read read write > write read read write ...". But of course, you should be able to pass an > infinite lseq to lseq-for-each and expect proc to be definitely called. > > Can this kind of semantic refinement go into post finalization notes? > > --shiro > > > > > On Sat, Feb 11, 2023 at 8:12 AM Wolfgang Corcoran-Mathe <[email protected]> > wrote: > >> Hi, >> >> The sample implementation of lseq-for-each has some surprising >> behavior. Here’s the implementation: >> >> (define (lseq-for-each proc . lseqs) >> (apply for-each proc (map lseq-realize lseqs))) >> >> Rather than traversing the lseqs and applying proc to their cars >> at each step, it fully forces them before traversing. There are >> two problems here. >> >> 1. Fully forcing the lseqs wastes space. A major reason to use >> lseqs is to avoid building huge lists. Thus, ignoring proc’s needs, >> lseq-for-each should operate in constant space. >> >> 2. Effects occur in an unexpected order. This is more interesting. >> Forcing the cdr of an lseq can have side effects, as can calling proc; >> indeed, proc is called for its side effects. When do these different >> effects occur? Consider this common kind of I/O stream: >> >> (define chars (generator->lseq read-char)) >> >> Calling ‘lseq-cdr’ on chars causes a side effect: reading a character. >> Now, combine this with an output procedure: >> >> (lseq-for-each write-char chars) >> >> The read and write effects could occur in (at least) two different >> orders: >> >> read >> write >> read >> write >> ... >> >> or >> >> read >> read >> ... >> read >> write >> write >> ... >> write >> >> The SRFI doesn’t tell us which to expect, but the latter, “clustered” >> pattern is bad. It will sponge up all available input before producing >> any output, hanging the program if ‘read-char’ blocks. “Interleaving” >> the effects, as in the first pattern, avoids these problems. >> >> *** >> >> The fix for both problems is simple: just implement ‘lseq-for-each’ >> directly. I’ll send a pull request with this implementation shortly. >> >> I think a note about effect order should also be added to the >> ‘lseq-for-each’ specification. Perhaps this: “If forcing the cdr of >> lseq entails side effects, then those effects are interleaved with >> the effects of calling proc.” >> >> Regards, >> >> Wolf >> >> -- >> Wolfgang Corcoran-Mathe <[email protected]> >> >
