Re: RFR[8238286]: 'Add new flatMap stream operation that is more amenable to pushing’

Anthony Vanelverdinghe dev at anthonyv.be
Thu Jun 25 17:12:20 UTC 2020


Hi

Given the signature of flatMap is:
<R> Stream<R> flatMap​(Function<? super T, ? extends Stream<? extends 
R>> mapper)

I'd like to propose the following signature for the new method:
<R> Stream<R> builderMap(BiConsumer<? super T, ? super 
Stream.Builder<R>> mapper)

This way both methods are named "...Map", and the name "builderMap" 
follows naturally from the argument's type.
If the given mapper invokes Stream.Builder::build, an 
IllegalStateException should be thrown.

Kind regards,
Anthony

On 25/06/2020 02:58, Paul Sandoz wrote:
> Hi,
>
> We traded CPS style for reusing existing functional interfaces. Originally the signature (my first choice) was as you indicate.
>
> By chance it just so happens that the current signature is the same shape as that for the accumulating argument type of the three arg collect terminal operation:
>
> Stream
> <R> R collect(Supplier<R> supplier,
>                BiConsumer<R, ? super T> accumulator,
>                BiConsumer<R, R> combiner);
>
> IntStream
> <R> R collect(Supplier<R> supplier,
>                ObjIntConsumer<R> accumulator,
>                BiConsumer<R, R> combiner);
>
> Same for the accumulator of a Collector too.
>
> However, I suspect you would argue these terminal accumulation cases are different from the intermediate case, as we are not accumulating but passing or accepting (loosely returning) zero or more elements that replace the input element.
>
> It’s my hope that generic specialization will allow the primitive stream types to fade into the background, along with the primitive functional interfaces. In that respect the addition of three functional interfaces for use on the primitive stream types is not so terrible.
>
>
> Regarding the name, you should have seen the first one :-) it was terrible.
>
> Here’s my few brush strokes on the bike shed. I wonder what people think of mapAccept. The specification talks about accepting elements, because that is the operative method name on Consumer. So we can say "T is replaced with the elements accepted by the Consumer<R>", or “ The Consumer<R> accepts the elements that replace T"
>
> Paul.
>
>
>
>> On Jun 24, 2020, at 1:01 PM, John Rose <john.r.rose at oracle.com> wrote:
>>
>> I like this new API point a lot; it allows flexible, local, temporary
>> control inversion in the context of one stream transform.
>>
>> What’s the performance model?  It seems like the SpinedBuffer
>> implementation makes a worst-case assumption about the number
>> of pending values, that there will be many instead of zero or one.
>>
>> But I guess the pipeline stuff already works in terms of pushes, so
>> the good news might be that this is really just a drill-down from the
>> user API into the kinds of operations (push-flavored) that go on
>> most of the time.
>>
>> OK, so I like the function but I have a beef with its bike shed
>> color.  First of all, this is a control-inversion (CPS) pattern,
>> which is very powerful but also notoriously hard to read.
>> I think that in Java APIs, at least in Stream APIs, code is
>> easier to read if the logical data flow is from left to right.
>>
>> (This is a language-specific observation.  Apart from varargs,
>> Java method APIs read favorably when extra control arguments
>> are added onto the end of the argument list.  Also, the convention
>> for generic functional interfaces is that the return value type
>> goes to the right, e.g., R in Function<A,R>.)
>>
>> So the BiConsumer is backwards, because the logical return
>> should be written, if not as a true return (which would appear
>> at the end of type parameter lists), at the end of the incoming
>> parameters (and in the last type parameter).
>>
>> I also think “multi” is needlessly “learned” sounding.  A simple
>> spatial preposition would work well: mapThrough, mapAcross, etc.
>> I think I prefer mapAcross because the term “across” can be read
>> adverbially: “we are mapping T across to Consumer<R>”.
>>
>> So:
>>
>> mapAcross(BiConsumer<? super T, Consumer<R>> mapper)
>> mapAcrossToInt(BiConsumer<? super T, IntConsumer> mapper)
>> mapAcross​(IntObjConsumer<IntConsumer> mapper)
>>
>> This does require additional FI’s like IntObjConsumer, but
>> I think that is a negligible cost.  Making the control inversion
>> *readable* is the high order bit here, not minimizing the number
>> of trivial FIs.
>>
>> (I almost hear David Bowman, in his space suit, saying, “My API…
>> It’s full of bikesheds!”  There’s a meme for that.)
>>
>> — John
>>
>> On Jun 24, 2020, at 3:57 AM, Patrick Concannon <patrick.concannon at oracle.com> wrote:
>>> Hi,
>>>
>>> Could someone please review myself and Julia's RFE and CSR for JDK-8238286 - 'Add new flatMap stream operation that is more amenable to pushing’?
>>>
>>> This proposal is to add a new flatMap-like operation:
>>>
>>> `<R> Stream<R> mapMulti(BiConsumer<Consumer<R>, ? super T> mapper)`
>>>
>>> to the java.util.Stream class. This operation is more receptive to the pushing or yielding of values than the current implementation that internally assembles values (if any) into one or more streams. This addition includes the primitive variations of the operation i.e. mapMultiToInt, IntStream mapMulti, etc.
>>>
>>> issue: https://bugs.openjdk.java.net/browse/JDK-8238286 <https://bugs.openjdk.java.net/browse/JDK-8238286>
>>> csr: https://bugs.openjdk.java.net/browse/JDK-8248166 <https://bugs.openjdk.java.net/browse/JDK-8248166>
>>>
>>> webrev: http://cr.openjdk.java.net/~pconcannon/8238286/webrevs/webrev.00/ <http://cr.openjdk.java.net/~pconcannon/8238286/webrevs/webrev.00/>
>>> specdiff: http://cr.openjdk.java.net/~pconcannon/8238286/specdiff/specout.00/overview-summary.html  <http://cr.openjdk.java.net/~pconcannon/8238286/specdiff/specout.00/overview-summary.html>
>>>
>>>
>>> Kind regards,
>>> Patrick & Julia


More information about the core-libs-dev mailing list