Am Do., 2. Sept. 2021 um 13:27 Uhr schrieb Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de>:
FORWARD #3

---------- Forwarded message ---------
Von: Daphne Preston-Kendal <xxxxxx@nonceword.org>
Date: Do., 2. Sept. 2021 um 11:31 Uhr
Subject: Re: SRFI 228: A further comparator library
To: Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de>


On 1 Sep 2021, at 14:38, Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:

> Am Mi., 1. Sept. 2021 um 14:08 Uhr schrieb Daphne Preston-Kendal <xxxxxx@nonceword.org>:
> […]
>> I found a semantic problem with using fold as the basis of this which seemed to me to be insurmountable. I have since forgotten what it was, though — I’ll take another look and get back to you.
>
> Thanks.  I would like to hear about it.

I remembered.

Consider two sequences, one of which is a prefix of the other. Pass both of them to fold, and fold will stop at the end of the shorter sequence (i.e. the one that is a prefix). There’s no way to tell which of them is longer and which should therefore sort later lexicographically.

Ah, yes.  And invoking the two folds individually is no option (unless you want to jump back and forth with a lot of call/cc machinery.

>> Yes, I too see this as a severe flaw of the SRFI 158 design, but alas I was not around at the time to argue against it. I would have, at the very least, made generators one-argument procedures rather than thunks, and had them return the argument passed when the sequence is finished, instead of the eof-object. Or had them return two values, the extra one being #t if there are more items in the sequence and #f if not.
> <snip>
> Your proposal doesn't remove the imperative nature.
>
> In what sense do you think my proposal is more fiddly?
>
> (define (generator->list gen)
>   (let f ((val (gen))
>     (if (eof-object? val)
>         '()
>         (cons val (f (gen)))))
>
> (define (iterator->list it)
>   (let f ((it it))
>     (it (lambda (it val)
>           (cons val (f it)))
>         (lambda () '()))))
>
> [Add any convenience syntax if you don't like explicit lambdas.]
>
> I don't see any consing here (except for the explicit "cons").  (And the closures of the two lambdas are constant.)

Okay, now I understand. Yes, you're correct, that would be a good, functional design. But I’m resistent to the idea of having two different iteration protocols in R7RS Large. My proposal could also, hypothetically, be retro-specced into the existing generator design: generators just have to assume the eof-object as the sentinel value if no explicit one is given.

While this proposal would be a conservative extension of the generator design, it would often lead to code like

(define (generator->list gen)
  (let ((sentinel (list 'sentinel)))
    (let f ((val (gen sentinel)))
      (if (eq? sentinel val)
          '()
          (cons val (f (gen sentinel)))))))

A design forcing such a code (allocating a sentinel on the heap and testing again for something the generator has already tested for, namely the end of the sequence) is IMO a bad language design.  For a library for private use, yes, but it shouldn't be codified in a standard that does not offer a better option at the same time.

I don't think that having two different iteration protocols in R7RS Large is that bad because they can occupy different niches.  (And R7RS Large already seems to try to offer something for every niche.)

The generator API can be used where the code is inherently imperative and where one knows the type of objects, e.g. in all situations resembling use cases of the prototypical generator "read".

The "iterator" API should be used in functional code dealing with abstract sequences (finite and infinite) and act as the glue.

This will finally also make the following statement in SRFI 158 true (when generator is replaced with iterator): "The combination of make-for-each-generator and generator-unfold makes it possible to convert any collection that has a for-each procedure into any collection that has an unfold constructor. This generalizes such conversion procedures as list->vector and string->list."