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