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