explode() (original) (raw)
Remi Forax forax at univ-mlv.fr
Tue Jan 29 13:57:35 PST 2013
- Previous message: explode()
- Next message: Hopefully the last message on Block
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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>) explodeToStream(Function<T,Stream>) 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 catcher); interface ShrapnelCatcher { void accept(U u); default void acceptAll(Collection 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, 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) and accept(Stream) 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 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>
- Previous message: explode()
- Next message: Hopefully the last message on Block
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the lambda-libs-spec-observers mailing list