<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">Hi R¨¦mi,</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);"><br>
</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">Thanks for your thoughts!</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);"><br>
</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">Improving the documentation of Gatherer / Gatherers is definitely something I am intending to have inspired
 by feedback received as people try it out, so your thoughts here are most welcome.</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);"><br>
</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">For the longest time Gatherer had Characteristics (just like Collector, which Gatherer borrows its design from)
 but in the end it didn't carry its weight and tracking the characteristics separately from the actual behavior turned out to be a source of both poor performance (since characteristics need to be composed) and a source of subtle defects.</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);"><br>
</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">The concern around the identity-equals of a lambda is a bit of a non-issue since we are comparing instances
 of the interfaces, and the default values are not lambdaform:https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/stream/Gatherers.java#L453</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);"><br>
</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">PS. Forgetting ofGreedy shouldn't affect semantics, it's just an evaluation hint which can result in higher
 performance.</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);"><br>
</span></div>
<div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">Cheers,</span></div>
<div class="elementToProof"><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">¡Ì</span></div>
<div 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">
<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"></div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>From:</b> core-libs-dev <core-libs-dev-retn@openjdk.org> on behalf of Remi Forax <forax@univ-mlv.fr><br>
<b>Sent:</b> Wednesday, 17 January 2024 16:38<br>
<b>To:</b> core-libs-dev <core-libs-dev@openjdk.java.net><br>
<b>Subject:</b> Improving the Gatherer public API</font>
<div> </div>
</div>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt;">
<div class="PlainText">Hello,<br>
i've played quite a lot with the Gatherer API and overall, i quite like how things work conceptually.<br>
But i think the public API can be improved.<br>
<br>
<br>
First, the documentation is not fully clear that a Gatherer has 3 characteristics,<br>
1/ Is it sequential or parallelizable (can be parallel is ask)<br>
2/ Is it stateless or stateful<br>
3/ Is the integrator greedy or not.<br>
<br>
There is also, is there a finisher or not, but this is less important.<br>
<br>
When creating a Gatherer, a user can find the right method "Gatherer.of" by following this state diagram<br>
<br>
    // if sequential<br>
      // if stateless<br>
        // if greedy<br>
        Gatherer.ofSequential(Gatherer.Integrator.ofGreedy(integrator), finisher?)<br>
        // otherwise short-circuit<br>
        Gatherer.ofSequential(integrator, finisher?)<br>
      // otherwise statefull<br>
        // if greedy<br>
        Gatherer.ofSequential(initializer, Gatherer.Integrator.ofGreedy(integrator), finisher?)<br>
        // otherwise short-circuit<br>
        Gatherer.ofSequential(initializer, integrator, finisher?)<br>
    // otherwise parallelizable<br>
      // if stateless<br>
        // if greedy<br>
        Gatherer.of(Gatherer.Integrator.ofGreedy(integrator), finisher?)<br>
        // otherwise short-circuit<br>
        Gatherer.of(integrator, finisher?)<br>
      // otherwise stateful<br>
        // if greedy<br>
        Gatherer.of(initializer, Gatherer.Integrator.ofGreedy(integrator), combiner, finisher)<br>
        // otherwise short-circuit<br>
        Gatherer.of(initializer, integrator, combiner, finisher)<br>
<br>
The first issue I stumble several time is that i've forgotten to use Integrator.ofGreedy(). I'm not the only one, at least both Viktor and Nicolai Parlog had the same issue. It's to easy to forget to call Integrator.ofGreedy().<br>
<br>
I think the API should be slightly modified to force the user to make a choice between greedy or short-cuircuit.<br>
<br>
I'm proposing to add a new parameter for all the factory methods, in front of the integrator, forcing the user to make a choice<br>
  enum IntegratorKind {<br>
    GREEDY, SHORT_CIRCUIT<br>
  }<br>
<br>
so the state diagram becomes<br>
<br>
    // if sequential<br>
      // if stateless<br>
        // if greedy<br>
        Gatherer.ofSequential(GREEDY, integrator, finisher?)<br>
        // otherwise short-circuit<br>
        Gatherer.ofSequential(SHORT_CIRCUIT, integrator, finisher?)<br>
      // otherwise stateful<br>
        // if greedy<br>
        Gatherer.ofSequential(initializer, GREEDY, integrator, finisher?)<br>
        // otherwise short-circuit<br>
        Gatherer.ofSequential(initializer, SHORT_CIRCUIT, integrator, finisher?)<br>
    // otherwise parallelizable<br>
      // if stateless<br>
        // if greedy<br>
        Gatherer.of(GREEDY, integrator, finisher?)<br>
        // otherwise short-circuit<br>
        Gatherer.of(SHORT_CIRCUIT, integrator, finisher?)<br>
      // otherwise stateful<br>
        // if greedy<br>
        Gatherer.of(initializer, GREEDY, integrator, combiner, finisher)<br>
        // otherwise short-circuit<br>
        Gatherer.of(initializer, SHORT_CIRCUIT, integrator, combiner, finisher)  <br>
<br>
The second issue is that it's hard to implement a Gatherer that itself need a Gatherer as parameter (like andThen/compose). This is due to the fact that querying if a Gatherer is SEQUENTIAL, STATELESS or GREEDY is far for obvious. Instead of having a method
 characteristics() like a Collector, the way to query the characteristics is very add-hoc, using either the default combiner/initializer or instanceof on the integrator.
<br>
<br>
  SEQUENTIAL: combiner == defaultCombiner<br>
  STATELESS:  initializer == defaultInitializer<br>
  GREEDY: integrator instanceof Greedy<br>
<br>
I think the API should be simple if the default combiner/initializer and finisher were removed a method characteristics (and a default method hasCHaracteristics) were added.<br>
It would be more like a Collector, simpler to user, and the default implementation of the combiner/initializer/finisher could be an impleemntation detail instead of being part of the API.<br>
<br>
As a side note: the exact semantics of == on a lambda is not specified so the actual implementation ask users to rely on the way the current OpenJDK implementation works.<br>
<br>
They are more mails to come on two other issues not fully realated to the public API of the Gatherer, so i'm trying to keep the mail short.<br>
<br>
regards,<br>
R¨¦mi<br>
</div>
</span></font></div>
</body>
</html>