explode()
Remi Forax
forax at univ-mlv.fr
Tue Jan 29 13:57:35 PST 2013
On 01/29/2013 07:19 PM, Brian Goetz wrote:
> The other alternative is more overloading:
>
> explode(Exploder<T,U>) // current
> explodeToCollection(Function<T,Collection<U>>)
> explodeToStream(Function<T,Stream<U>>)
> explodeToArray(Function<T,U[]>)
>
> Note that many people think "all they want" is the second one, but
> they're wrong.
>
> IF we can only have one, there's no discussion -- it's the first one,
> since one can relatively easily derive the others from the first.
> However, the first does tend to induce head-explosion.
like Collector, you can have one explode(Exploder<T,U>) and several way
to create Exploders
like explode(Exploder.toCollection(User::getPermissions))
Rémi
>
>
>
> On 1/29/2013 1:14 PM, Brian Goetz wrote:
>>> Hmm. So the methods I proposed, Consumer.accept(Collection) and
>>> Consumer.accept(Stream), are functionally identical to
>>> Collection.forEach(consumer) and Stream.forEach(consumer), and thus
>>> have
>>> little benefit except in that various Consumer implementations might be
>>> able to override them and do something more efficient. Which in turn
>>> means that these forEach() methods would have to delegate straight to
>>> them; they become pure conveniences only.
>>
>> Mostly so, though I could imagine (I realize I'm undermining my earlier
>> position) that Sink.acceptAll(Stream) might be able to do something
>> better in the case when we explode an element to an infinite stream.
>> Currently, exploding an element to an infinite stream:
>>
>> ints.explode(i -> Streams.repeat(() -> i)).getFirst()
>>
>> would never terminate, even though it theoretically could.
>>
>> Having either the acceptAll() method on Consumer OR the send(Stream)
>> method on DownstreamThingie, would leave us a door to eventually fix
>> that issue; asking users to do stream.forEach(consumer) would make it
>> harder to do so.
>>
>> My worry is that, especially given the late date, I'd like to minimize
>> the number of default methods on SAM types.
>>
>>> But when considering those use cases, one of them may arise if drop
>>> Downstream and have explode() use Consumer instead. In that case, does
>>> it break explode() if we have users just using stream.forEach(consumer)
>>> and collection.forEach(consumer) instead of downstream.send(stream) and
>>> downstream.send(collection)?
>>
>> Another possibility is to return to an intermediate position, where
>> there was a SAM for "function from singleton to multi", such as the
>> Exploder you suggest. Once we do that, then putting a renamed
>> Downstream in Exploder makes it clearer:
>>
>> interface Exploder<T,U> {
>> void explode(T input, ShrapnelCatcher<U> catcher);
>>
>> interface ShrapnelCatcher<U> {
>> void accept(U u);
>> default void acceptAll(Collection<U> c) { ... }
>> ...
>> }
>> }
>>
>>
>>
>>>
>>>
>>> On Thu, Jan 24, 2013 at 2:43 PM, Kevin Bourrillion <kevinb at google.com
>>> <mailto:kevinb at google.com>> 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>
>>>
>>>
>>>
>>>
>>> --
>>> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com
>>> <mailto:kevinb at google.com>
More information about the lambda-libs-spec-observers
mailing list