Language feature to support Fluent Syntax for Static Method Calls

Brian Goetz brian.goetz at oracle.com
Tue Mar 28 19:10:00 UTC 2023


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/80bfc037/attachment-0001.htm>


More information about the amber-dev mailing list