explode()
Brian Goetz
brian.goetz at oracle.com
Thu Jan 24 16:27:05 PST 2013
So, you sort of came full circle to where we started, where we passed a
Sink/Block/Consumer representing the downstream. The big complaint
against that was "What if I already have a Collection? I have to
iterate it myself?"
I'm unwilling to lard Sink/Block/Consumer with these extra methods just
to avoid the extra interface; they're not intrinsic enough to
Sink/Block/Consumer to be worth the confusion. Though we could have an
interface that extends S/B/C that adds the extra methods.
Also, there's something stream-specific to the current Downstream; while
the default for send(Stream) just delegates to forEach, this will fail
if an element is mapped to an infinite stream. However, a better
implementation of send(Stream) could prevent this. I want to leave that
door open.
So, adding this all up:
interface Exploder<T, U> {
void explodeInto(T input, FragmentCollector<U> sink);
}
interface FragmentCollector<U> extends Sink<U> { // or better name
// with accept methods for array/stream/Iterable
}
<U> Stream<U> explode(Exploder<T,U>)
Is this any better than the status quo?
On 1/24/2013 5:43 PM, Kevin Bourrillion wrote:
> explode() is aptly named for what it did to my brain when I encountered
> it. :-)
>
> A few things are adding up to make it impenetrable.
>
> 1. The vast majority of the Stream API, save the obvious exceptions like
> forEach(), is solidly in the functional style. Collectors aren't really,
> but as long as I use the prepackaged ones, it /feels/ fairly functional
> anyway. Now suddenly there's explode, which doesn't feel functional at
> all. Instead, I need to think of the bit of code I provide to it as an
> active participant in a pipeline. Things are fed to me, I feed things on
> down the line. This makes it different, and different is automatically
> confusing. This can't really be /corrected/, but seems worth nothing;
> maybe we can account for the difference somehow.
>
> 2. In attempting to learn it, I'm confronted with a type,
> Stream.Downstream<U>, which I won't have heard of before that moment,
> and whose name reveals little. Is there any particular reason that
> Sink/Consumer, with its accept(T) method, should not also have "default"
> methods implementing accept(Collection<T>) and accept(Stream<T>) and
> accept(T[])? I can't think of any; those sound just plain handy. And
> if we added those, would there be enough value in preserving Downstream
> as an identically-signatured type? We could dispense with it, as I've
> done below.
>
> 3. Except sometimes there /is/ value in having a special type even if
> its signature is identical to another. I suspect that a custom type
> could help quite a bit in this case:
>
> interface Exploder<T,R> {
> /**
> * Maps {code input} to zero or more instances of R, sending these
> * to {@code resultConsumer} to be included in the results of
> * {@link Stream#explode(Exploder)}.
> */
> void explode(T input, Consumer<R> resultConsumer);
> }
>
>
> 4. Then there is the name "explode". It's... okay. "unpack"? Nah. It
> seems maybe 40% possible that a great name is out there eluding us....
>
>
>
> --
> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com
> <mailto:kevinb at google.com>
More information about the lambda-libs-spec-experts
mailing list