Implementation of lseq-for-each Wolfgang Corcoran-Mathe (11 Feb 2023 18:11 UTC)
Re: Implementation of lseq-for-each Shiro Kawai (12 Feb 2023 00:05 UTC)
Re: Implementation of lseq-for-each John Cowan (12 Feb 2023 15:47 UTC)
Re: Implementation of lseq-for-each Wolfgang Corcoran-Mathe (13 Feb 2023 18:38 UTC)
Re: Implementation of lseq-for-each Arthur A. Gleckler (14 Feb 2023 04:27 UTC)
Re: Implementation of lseq-for-each John Cowan (17 Feb 2023 21:46 UTC)
Re: Implementation of lseq-for-each Wolfgang Corcoran-Mathe (27 Feb 2023 23:01 UTC)
Re: Implementation of lseq-for-each John Cowan (28 Feb 2023 00:37 UTC)

Implementation of lseq-for-each Wolfgang Corcoran-Mathe 11 Feb 2023 18:11 UTC

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  <xxxxxx@sigwinch.xyz>