Hi Anthony

Great questions! I had typed up a long response when my email client decided 
the email was too large, crashed, and deleted my draft, so I'll try to recreate 
what I wrote from memory.

>While I understand that most Gatherers will be reusable, and that it's a 
>desirable characteristic, surely there will also be non-reusable Gatherers?

To me, this is governed by the following parts of the Gatherer 
specification<https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html>:

"Each invocation of 
initializer()<https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#initializer()>,
 
integrator()<https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#integrator()>,
 
combiner()<https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#combiner()>,
 and 
finisher()<https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#finisher()>
 must return a semantically identical result."

and

"Implementations of Gatherer must not capture, retain, or expose to other 
threads, the references to the state instance, or the downstream 
Gatherer.Downstream<https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.Downstream.html>PREVIEW<https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.Downstream.html#preview-java.util.stream.Gatherer.Downstream>
 for longer than the invocation duration of the method which they are passed 
to."

And I think the worst of all worlds would be a scenario where you, as a user, 
are given a Gatherer<X,Y,Z> and you have no idea whether you can re-use it or 
not.

For Stream, the assumption is that they are NOT reusable at all.
For Gatherer, I think the only reasonable assumption is that they are reusable.

>In particular, any Gatherer that is the result of a factory method with a 
>`Stream<T>` parameter which supports infinite Streams, will be non-reusable, 
>won't it?

Not necessarily, if the factory method consumes the Stream and creates a stable 
result which is reusable, then the resulting Gatherer is reusable.

>In a previous response you proposed using `Gatherer 
>concat(Supplier<Stream<T>>)` instead, but then I'd just pass `() -> aStream`, 
>wonder why the parameter isn't just a `Stream<T>`, and the Gatherer would 
>still not be reusable.

There's a very important, to me, difference between the two. In the 
Stream-case, there exists 0 reusable usages. For the Supplier<Stream>-case the 
implementation does not restrict re-usability, but rather it is up to the 
caller to actively opt-out of reusability (which could of course also be 
declared to be undefined behavior of the implementor of said Gatherer). Local 
non-reusability decided by the caller > Global non-reusability decided by the 
callee.

>As another example, take Gunnar Morling's zip Gatherers:
I don't see how Gatherers like this could be made reusable, or why that would 
even be desirable.

Having been R&D-ing in the Stream-space more than a decade, I'm convinced that 
there's no universally safe way to implement `zip` for push-style stream 
designs. I'm happy to be proven wrong though, as that would open up some 
interesting possibilities for things like Stream::iterator() and 
Stream:spliterator().

>My use case was about a pipeline where the concatenation comes somewhere in 
>the middle of the pipeline.

My apologies, I misunderstood. To me, the operation you describe is called 
`inject`.
Given a stable (reusable) source of elements you can definitely implement 
Gatherers which do before, during, or after-injections of elements to a stream.

Thanks again for the great questions and conversation, it's valuable!

Cheers,
√


Viktor Klang
Software Architect, Java Platform Group
Oracle

Reply via email to