Doing a bit of thinking out loud on this:

1. The "Lazy" postfix to me seems to indicate very clearly what trait
   these family of methods share and why they are here.
    1. I also feel that the methods would not be used that often, since
       for typical collection sizes performance differences should be
       negligible.
    2. Therefore I feel that adding 4 letters at the end would be ok here.
2. "It" for me indicates the English word "it" (German: "es"), not a
   short form of Iterator.
    1. That also coincides with the "it" name for an implicit single
       closure argument in Groovy.
        1. (In our framework we e.g. also use "it" with similar
           semantics as the name for a singleton/root class member.)
    2. "Iter" could potentially be used to get around that, but I would
       prefer "Lazy" also in this case, since it could be overloaded to
       be used for something else than an iterator without the need to
       introduce new method names.
3. While "Then" as a postfix implies a chain of operations, I do not
   see where it implies these operations being lazy (?).
    1. So while it would be viable, "Lazy" would also be the winner
       here in my book.
4. filter, map, flatMap:
    1. While these are widely used in other languages, their Groovy
       counterparts are more intuitive to me (Note: I knew these before
       I encountered Groovy).
        1. (The only exception here is Groovy's "collectMany", which
           should imho be "collectFlattened" o.s. (but also not
           "collectFlat") ).
    2. So I feel they should probably continue to be confined to the
       "Java source code compatibility" corner of Groovy :-)

Cheers,
mg


On 09/04/2025 13:21, Jochen Theodorou wrote:
On 09.04.25 03:25, Paul King wrote:
Hi folks,

[I sent this to grails dev list but meant to send it here and CC them
for feedback - anyway, it is here now, apologies if you see this
twice.]

I have been looking at the functionality in Groovy-stream[1] and
Gatherers4J[2] lately with a view to filling any gaps in Groovy's
iterator DGM methods. I'm not trying to replicate everything they
contain, just looking for the most useful functionality Groovy might
be missing.

The biggest missing pieces at this point in my mind are lazy (Iterator
return value) variants of findAll, collect, and collectMany. Groovy's
current variants are eager (return collections and lists).
Groovy-stream gets around this by adding stream-named variants:
filter, map, and flatMap.


does Iterator really automatically mean lazy? Is this possible then?

def l = [1,2,3]
def iterator = l.iterator().findAll{it>1}
l[0] = 20
assert iterator.toList() == [20,2,3]

The combination of lazy and mutable can be problematic. Also, can I do
iterator.toList() multiple times?

One option is to break backwards compatibility (Groovy 5 only). So
only for the versions of those methods which take an Iterator as
input, change the return type to be Iterator. Given how widely used
those methods are, I don't think that is an option for us.

well... we could have lazyIt() which return a LazyIterator. that would
make things clear.

Actually, findAll currently doesn't have an Iterator variant, so we
could add that but it would still be a behavioral compatibility
breakage since the Object version is used for Iterators and it returns
a list.

So, we could give up on lazy variants for those methods, but again
given how commonly used they are, that is a pretty big gap.

So, the other option is to provide alternative names. The best to me seem:

(A) findAllLazy, collectLazy, collectManyLazy
(B) findAllThen, collectThen, collectManyThen
(C) filter, map, flatMap
(D) something else?

(E) lazyIt()

I am pretty sure it will not be considered as because it is a bit
ugly... but frankly I am no fan of A at all for the very same reason. B
I find more nice because it is more semantics based.

hmm... what happens if you mix lazy and non-lazy... like for example
findAllthen(...).findAll(...)

Option (C) is what Groovy-stream did and would be familiar to Java
Stream users but folks are going to ask, why can't I have that "alias"
for Iterables and arrays, but the intent here is just for the Iterator
variants. I think Lazy best conveys that. Use without "Lazy" for the
eager (think terminal operator) variant and with "Lazy" for the lazy
(think intermediate operator) variant. It also is easier to extend,
the fourth method in terms of gaps is collectEntries, which currently
returns a Map. An Iterator<Map.Entry> return value could be made for
collectEntriesLazy if we wanted.

somehow does not really convince me all.

Note that many of our operators are terminal in nature, find, count*,
inject, etc, so this isn't about doing this for all operators
eventually.

but findAllThen does not come over as terminal. If they are supposed to,
then I don't find the names fitting in the cases of A and B.

bye jochen

Reply via email to