<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Hi Anthony,</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Bear with me for a moment,</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
in the same vein as there's nothing which <i>enforces</i> equals(¡K) or hashCode() to be conformant to their specs, or any interface-implementation for that matter, I don't see how we could make any stronger enforcement of Gatherers.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
>My belief is that the subject of reusability hasn't come up before because non-reusable Gatherers "just work": as long as instances of such Gatherers are not reused, they don't lead to unexpected results or observable differences in behavior. And so people
 have been implementing non-reusable Gatherers such as `concat` and `zip` without realizing they aren't compliant. Or maybe they did realize it, but didn't see the downside of being non-compliant.<br>
<br>
Alas, there's no place where this could be enforced, users could have their own implementations of Stream (so cannot be enforced in Stream::gather). Ultimately, it all boils down to specification¡Xif an equals(¡K)-method implementation leads to surprising behavior
 when used with a collection, one typically needs to first ensure that the equals(¡K)-method conforms to its expected specification before one can presume that the collection has a bug.<br>
<br>
For the "just work"¡Xscenario, one can only make claims about things which have been proven. So in this case, what tests have passed for the implementation in question?</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
>Which brings me to my next point: in case of (b), the Javadoc and/or JEP should explain the rationale. Even to me it still seems like a needless restriction. <br>
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
java.util.stream.Stream does not explain the rationale for why it is single-use, Collector does not explain why they are reusable, why would Gatherers be held to a different standard?</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
> "protecting the users from being given non-reusable Gatherers"<br>
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Think of it more like increasing the odds that users are given spec-conformant Gatherers.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div id="Signature" style="color: inherit;">
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Cheers,<br>
¡Ô</div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<b><br>
</b></div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<b>Viktor Klang</b></div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Software Architect, Java Platform Group<br>
Oracle</div>
</div>
<div id="appendonsend" style="color: inherit;"></div>
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<hr style="display: inline-block; width: 98%;">
<div id="divRplyFwdMsg" dir="ltr" style="color: inherit;"><span style="font-family: Calibri, sans-serif; font-size: 11pt; color: rgb(0, 0, 0);"><b>From:</b> Anthony Vanelverdinghe <dev@anthonyv.be><br>
<b>Sent:</b> Wednesday, 18 September 2024 18:27<br>
<b>To:</b> Viktor Klang <viktor.klang@oracle.com>; core-libs-dev@openjdk.org <core-libs-dev@openjdk.org><br>
<b>Subject:</b> [External] : Re: Stream Gatherers (JEP 473) feedback</span>
<div> </div>
</div>
<div style="font-size: 11pt;">Hi Viktor<br>
<br>
Let me start with a question: is the requirement (a) "a Gatherer SHOULD be reusable", or (b) "a Gatherer MUST be reusable"?<br>
As of today the specification says (b), whereas the implementation matches (a).<br>
<br>
In case of (a), I propose to align the specification to allow for compliant, non-reusable Gatherers.<br>
<br>
In case of (b), I propose to align the implementation to enforce compliance. Something like:<br>
(1) invoke `initializer()` twice, giving `i1` and `i2`. Discard `i1` and invoke `i2` twice, giving `state1` and `state2`.<br>
(2) invoke `finisher()` twice, giving `f1` and `f2`. Discard `f1` and invoke `f2` twice, the first time with `state1` and a dummy Downstream, the second time with the actual final state, i.e. `state2` after all elements were integrated, and the actual Downstream.<br>
Then backport this change to JDK 23 & 22 and/or do another round of preview in JDK 24.<br>
I'm confident that enforcing compliance would result in significant amounts of feedback questioning the requirement.<br>
<br>
My belief is that the subject of reusability hasn't come up before because non-reusable Gatherers "just work": as long as instances of such Gatherers are not reused, they don't lead to unexpected results or observable differences in behavior. And so people
 have been implementing non-reusable Gatherers such as `concat` and `zip` without realizing they aren't compliant. Or maybe they did realize it, but didn't see the downside of being non-compliant.<br>
<br>
Which brings me to my next point: in case of (b), the Javadoc and/or JEP should explain the rationale. Even to me it still seems like a needless restriction. You say:<br>
<br>
> 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.<br>
<br>
so I'd guess the rationale is "protecting the users from being given non-reusable Gatherers".<br>
However, I can't readily think of a situation where this would be essential.<br>
If a user creates a Gatherer by invoking a factory method, the factory method can specify whether its result is reusable.<br>
And if a user is given a Gatherer as a method argument, and they need the Gatherer to be reusable, they could change the parameter to a `Supplier<Gatherer>` instead.<br>
<br>
> >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.<br>
><br>
> 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.<br>
<br>
We agree, just that I'd provide 2 factory methods, `concat(Stream<T>)` (non-reusable) and `append(List<T>)` (reusable), whereas you'd provide a 2-in-1 `concat(Supplier<Stream<T>>)`.<br>
<br>
Kind regards, Anthony<br>
<br>
September 12, 2024 at 11:55 PM, "Viktor Klang" <viktor.klang@oracle.com> wrote:<br>
><br>
> Hi Anthony<br>
><br>
> 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.<br>
><br>
> >While I understand that most Gatherers will be reusable, and that it's a desirable characteristic, surely there will also be non-reusable Gatherers?<br>
><br>
> To me, this is governed by the following parts of the Gatherer specification <a href="https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html" id="OWA46ca0d84-2809-a1e2-6e71-ac045d94da42" class="OWAAutoLink" data-auth="NotApplicable">
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html</a> :<br>
><br>
> "Each invocation of initializer() <a href="https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#initializer()" id="OWA0ce29f6a-dadc-12cd-2d65-873d517b53b7" class="OWAAutoLink" data-auth="NotApplicable">
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#initializer()</a> ,integrator()
<a href="https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#integrator()" id="OWA4dbb8958-4e13-7871-53f9-5b6823be2dde" class="OWAAutoLink" data-auth="NotApplicable">
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#integrator()</a> ,combiner()
<a href="https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#combiner()" id="OWAc35f0ccb-e19b-9884-00a4-e20ec44447aa" class="OWAAutoLink" data-auth="NotApplicable">
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#combiner()</a> , and finisher()
<a href="https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#finisher()" id="OWAee7fc868-ea7b-e46e-d3c1-16de767b8c6d" class="OWAAutoLink" data-auth="NotApplicable">
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.html#finisher()</a>  must return a semantically identical result."<br>
><br>
> and<br>
><br>
> "Implementations of Gatherer must not capture, retain, or expose to other threads, the references to the state instance, or the downstreamGatherer.Downstream
<a href="https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.Downstream.html" id="OWA3d484167-b706-a4d1-6967-80ef5f7ca646" class="OWAAutoLink" data-auth="NotApplicable">
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.Downstream.html</a> PREVIEW
<a href="https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.Downstream.html#preview-java.util.stream.Gatherer.Downstream" id="OWA08825e49-35d3-079c-e6fa-a0389247b619" class="OWAAutoLink" data-auth="NotApplicable">
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/stream/Gatherer.Downstream.html#preview-java.util.stream.Gatherer.Downstream</a>  for longer than the invocation duration of the method which they are passed to."<br>
><br>
> 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.<br>
><br>
> For Stream, the assumption is that they are NOT reusable at all.<br>
> For Gatherer, I think the only reasonable assumption is that they are reusable.<br>
><br>
> >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?<br>
><br>
> Not necessarily, if the factory method **consumes** the Stream and creates a stable result which is reusable, then the resulting Gatherer is reusable.<br>
><br>
> >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.<br>
><br>
> 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.<br>
><br>
> >As another example, take Gunnar Morling's zip Gatherers: <br>
><br>
> I don't see how Gatherers like this could be made reusable, or why that would even be desirable.<br>
><br>
> 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().<br>
><br>
> >My use case was about a pipeline where the concatenation comes somewhere in the middle of the pipeline.<br>
><br>
> My apologies, I misunderstood. To me, the operation you describe is called `inject`.<br>
> Given a stable (reusable) source of elements you can definitely implement Gatherers which do before, during, or after-injections of elements to a stream.<br>
><br>
> Thanks again for the great questions and conversation, it's valuable!<br>
> Cheers,<br>
><br>
> ¡Ô<br>
><br>
> **Viktor Klang**<br>
> Software Architect, Java Platform Group<br>
><br>
> Oracle<br>
></div>
</body>
</html>