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