Language feature to support Fluent Syntax for Static Method Calls
Tagir Valeev
amaembo at gmail.com
Tue Mar 28 19:58:10 UTC 2023
> The second example -- changing how parallel execution works -- requires
reinventing almost all of the implementation of Streams (which, if you've
never looked at it, is a lot more complicated than you might think.) In
which case the surface expression here is the least of your problems.
Sorry for moving the discussion away but I cannot stay aside when there's
Stream API on the table :-) Implementing .parallel(fjp) (fjp, not just any
executor) is not that hard as it seems. The only thing you need is to
create a tiny wrapper delegate over the original stream that remembers the
supplied fjp, and then submit every terminal operation to that fjp and join
the result. I implemented this in my StreamEx library [1], and this one is
definitely not the hardest Stream API extension that I implemented.
That said, as you need to wrap Stream API anyway, you can make it quite
comfortable without extension methods. You need to create a bunch of
factories repeating stream sources from JDK, like StreamEx.of(Collection)
instead of Collection.stream(). Not so huge work either. And then you can
add .toSet() :-)
With best regards,
Tagir Valeev.
[1]
https://www.javadoc.io/static/one.util/streamex/0.8.1/one.util.streamex/one/util/streamex/AbstractStreamEx.html#parallel(java.util.concurrent.ForkJoinPool)
On Tue, Mar 28, 2023 at 9:11 PM Brian Goetz <brian.goetz at oracle.com> wrote:
> Not to pick on your example, but I'm going to pick on your example....
>
> You give as examples two methods you'd like to add to Stream: toSet and
> parallel(Executor) -- and it is notable that these examples are fairly
> commonly cited when this topic comes up. Note that the first is entirely a
> cosmetic thing; we already have collect(Collectors::toSet), so all this
> does is save a few characters -- its just code golf.
>
> The second example -- changing how parallel execution works -- requires
> reinventing almost all of the implementation of Streams (which, if you've
> never looked at it, is a lot more complicated than you might think.) In
> which case the surface expression here is the least of your problems.
>
> Now, it's easy for someone to complain "why didn't they make streams
> extensible" (we actually spent a lot of time exploring how this might
> work), but the reality is, Streams does not actually let users plug in new
> operations except through defined extension points like collect(),
> regardless of how easy or hard the language would make that. And the
> tricks that would create the illusion of doing so, like extension methods,
> force you to give up a significant portion of the nonfunctional value of
> streams, because a static "extension" method can't fuse operations, can't
> access the parallel machinery used by the rest of streams, can't interact
> with short-circuiting easily, can't take advantage of in-place
> optimizations, etc. So making a "static" extension look like a built-in
> method with chaining actually obfuscates what is going on, depriving
> readers of cues about the runtime behavior.
>
> Returning to your question, the problem of "wrapping streams" is one of
> the streams framework having a significant amount of complexity under the
> hood, which makes "tapping into it" hard -- and that's the real problem.
> And -- and here's the kicker -- this complexity shows up in most APIs that
> are candidates for heavy use of chaining anyway.
>
>
>
>
>
> On 3/28/2023 2:51 PM, Archie Cobbs wrote:
>
> On Tue, Mar 28, 2023 at 10:48 AM Ron Pressler <ron.pressler at oracle.com>
> wrote:
>
>> As usual, the main challenge is understanding what exactly is the problem
>> here — is this a specific issue with CF and Stream or something more
>> general — and if there is a general problem, what exactly is it, and does
>> it justify a change to the language. Only after we answer that can we
>> consider adding a language feature.
>>
>
> Great point - which also makes me curious how we should define the
> underlying problem here.
>
> One problem is "prettier chaining" which as Brian pointed out makes for a
> relatively weak case.
>
> What about another problem, which is that in Java it's too hard to "wrap"
> something with new functionality? I.e., this is the same problem extensions
> try to solve.
>
> Just to be clear, suppose I invent this (using Kristofer's example):
>
> public interface BetterStream<T> extends Stream<T> {
> BetterStream<T> parallel(Executor e)
> Set<T> toSet()
> @Override
> BetterStream<T> filter(Predicate<? super T> pred) // etc.
> }
>
> It's not easy to wrap Streams I encounter to convert them into
> BetterStreams. I agree with Brian that "API designers should control their
> API's" so I suppose we're talking about a true "wrap", not a "monkey
> patch". You can do a "wrap" today but it's tedious and brittle. Could the
> language make it easier somehow?
>
> I'm sure this has been discussed before. Curious what's the current status
> of that discussion.
>
> -Archie
>
> --
> Archie L. Cobbs
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230328/d6f549fd/attachment-0001.htm>
More information about the amber-dev
mailing list