<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<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 Cay,<br>
<br>
No problems at all! 🙂 Sharing the background is interesting because people rarely can see "what didn't work" or "what led to this".</div>
<div id="Signature" class="elementToProof" style="color: inherit;">
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
>I don't understand why a short-circuiting integrator is "more capable" than a greedy one. And why the "less capable" one would then be the subclass.</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 style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Being able to consume Set(0..N) elements is a much larger set than Set(N), or in other terms, being able to (but not necessarily having to) terminate before consuming an entire stream makes the operation able to execute on top of "infinite" streams, greedy
operations wouldn't terminate in such cases.</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 style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
So if a "short-circuiting" operation can consume potentially any number of elements, and a "greedy" operation only can consume all elements, and all-elements is a length subset of "any prefix"—so it is "less capable".<br>
<br>
>But what about implementing a gatherer that never short-circuits, like all three gatherers in the JEP? Here, "ofGreedy" is an indication that an optimization can be made (pushing vs. pulling), right? And that's what we want the implementor to choose.</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 style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Not necessarily push-vs-pull (Java Streams are by nature push-streams) but rather the cost of having to track/inspect short-circuiting across N stages of processing turns out to be a non-negligible cost so if you don't want to pay for it, you can opt out. For
short-circuiting operations it's a cost you're willing to pay in order to be able to only consume a prefix of the stream.<br>
<br>
>That's where my struggle comes in. How can I phrase my advice in a symmetric way? Right now, my advice is "if the integrator doesn't short-circuit, advertise that benefit by using ofGreedy" and "if the gatherer can't combine results, advertise that drawback
by using ofSequential".</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 style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Ah, I see what you're thinking there. I tend to try to want to frame things like that from multiple vantage points, so to me it would look something like this:<br>
<br>
Implementor: "use ofGreedy to signal that this operation does not itself initiate short-circuiting"<br>
Reviewer: "when you see ofGreedy verify that this operation does not itself initiate short-circuiting"<br>
Debugger: "when the expectation was that the stream should be short-circuiting and isn't, one thing to look for is ofGreedy"<br>
<br>
Implementor: "use ofSequential to signal that this operation is intended to always be executed sequentially"<br>
Reviewer: "when you see ofSequential, understand why the operation can't/shouldn't be parallelized for parallel streams"<br>
Debugger: "if the parallel stream is not performing as well as expected, one thing to look for is ofSequential!</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 style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Many times, it's the same <b>person</b> in the different roles at different times, so understanding what the signal indicates to each role is good.<br>
<br>
>Put the factory method first in the JEP :-)</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 style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
I'd expect most Java developers to encounter this via the Javadoc and I put the factory as the main example:
<a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/util/stream/Gatherer.html" id="LPlnk175011" class="OWAAutoLink">
https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/util/stream/Gatherer.html</a></div>
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
<br>
>1. The way to tell whether a gatherer is sequential is very unusual. To provide a sequential gatherer I must fail to override a default method. I don't recall seeing anything like this in the Java API.<br>
<br>
To be fair, the "interface with only methods returning functional interfaces" is a pretty rare sight as well? Using sentinel values turned out to be the low-energy state solution:</div>
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
By default a Gatherer is stateless, if you need state, you need to provide a Supplier which creates it.<br>
By default a Gatherer is sequential, if you need parallelization, you need to provide a BinaryOperator which provides it.<br>
By default a Gatherer does nothing on end-of-stream, if you need to do something then, you need to provide a BiFunction which does it.<br>
<br>
>2. The way to tell whether an integrator is greedy is through a subinterface. That's fine, except it's a marker interface with no new methods. And then why not use a subinterface for parallelizable gatherers, where one could make combiner a subinterface method?
Too OO for 2024?<br>
<br>
What about stateless Gatherers? Is that a marker interface too? I've been down this rabbit hole a couple of times and have the scars to prove it. 🙂<br>
<br>
>3. Here we have two different mechanisms for what streams and collectors address with a simple and unified approach: flags.</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 style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
I guess it bears repeating: Please see the discussion around why Characteristics didn't make it. 🙂<br>
<br>
>I don't strongly care about this, just answering your question.<br>
<br>
Fair enough!<br>
<br>
</div>
<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> Cay Horstmann <cay.horstmann@gmail.com><br>
<b>Sent:</b> Tuesday, 15 October 2024 16:07<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: Fw: New candidate JEP: 485: Stream Gatherers</span>
<div> </div>
</div>
<div style="font-size: 11pt;">Hi Viktor, thanks again for sharing your design decisions. I agree this is a minor matter because not many programmers will implement gatherers. Let me try to explain my confusion.<br>
<br>
On 15/10/2024 13:46, Viktor Klang wrote:<br>
<br>
> Making of() the short-name is because it is more capable than ofGreedy() (which does not support short-circuiting). So using it signals a choice of less capability.<br>
<br>
I don't understand why a short-circuiting integrator is "more capable" than a greedy one. And why the "less capable" one would then be the subclass.<br>
<br>
What is the capability? To never short-circuit? To signal short-circuiting?<br>
<br>
> The reason for ofSequential/of instead of ofParallel/of is that a parallel-capable Gatherer covers more scenarios than a sequential-only versison, so giving the shorter name to the less-capable version would not stand out in the same way when reviewing, and
Collector.of is parallel-capable, which seemed more consistent to make Gatherer.of parallel-capable as well.<br>
<br>
Again, what is "less capable"?<br>
<br>
When you write "not stand out in the reviewing", I interpret that as follows: "of" means the mechanism providing maximum efficiency, that we want implementors to use. And "ofXXX" means they didn't go all the way, either because of the nature of the processing
or because they didn't take the trouble to do all the work.<br>
<br>
With of/ofSequential, I can see that point of view. "ofSequential" is a warning that, when used with a parallel stream, there is the potential for disappointment.<br>
<br>
But what about implementing a gatherer that never short-circuits, like all three gatherers in the JEP? Here, "ofGreedy" is an indication that an optimization can be made (pushing vs. pulling), right? And that's what we want the implementor to choose.<br>
<br>
That's where my struggle comes in. How can I phrase my advice in a symmetric way? Right now, my advice is "if the integrator doesn't short-circuit, advertise that benefit by using ofGreedy" and "if the gatherer can't combine results, advertise that drawback
by using ofSequential".<br>
<br>
Or maybe I misunderstand greedy?<br>
<br>
><br>
> >First off, I think factory methods should be the favored approach.<br>
><br>
> Is there something which could be made clearer around that, because that's how I already view it.<br>
<br>
Put the factory method first in the JEP :-)<br>
<br>
> >The details are fussy, with the marker interface and the magic default combiner.<br>
><br>
> I think this is worth expanding a bit on, in what way are they fussy?<br>
><br>
<br>
Maybe fussy is not a good term. Baroque? This is what went through my head when I studied the design.<br>
<br>
1. The way to tell whether a gatherer is sequential is very unusual. To provide a sequential gatherer I must fail to override a default method. I don't recall seeing anything like this in the Java API.<br>
<br>
2. The way to tell whether an integrator is greedy is through a subinterface. That's fine, except it's a marker interface with no new methods. And then why not use a subinterface for parallelizable gatherers, where one could make combiner a subinterface method?
Too OO for 2024?<br>
<br>
3. Here we have two different mechanisms for what streams and collectors address with a simple and unified approach: flags.<br>
<br>
I don't strongly care about this, just answering your question.<br>
<br>
Cheers,<br>
<br>
Cay<br>
<br>
--<br>
<br>
Cay S. Horstmann | <a href="https://urldefense.com/v3/__https://horstmann.com__;!!ACWV5N9M2RV99hQ!NoHCnee1zi29L19LosVsyGdjFa1TJ9Ioszv2U02jMGnoWjQbyoGMCq4d8tv7O--v6s6gLgqOGfNr1RwC2sOUl5MfHQ$" id="OWAcb2f9ee8-0990-0d9a-febc-8f9191b4ba4b" class="OWAAutoLink" data-auth="NotApplicable">
https://urldefense.com/v3/__https://horstmann.com__;!!ACWV5N9M2RV99hQ!NoHCnee1zi29L19LosVsyGdjFa1TJ9Ioszv2U02jMGnoWjQbyoGMCq4d8tv7O--v6s6gLgqOGfNr1RwC2sOUl5MfHQ$</a><br>
<br>
</div>
</body>
</html>