explode()

Kevin Bourrillion kevinb at google.com
Thu Jan 24 18:26:15 PST 2013


Oh, it would not look that different:

List<Track> allTracks = albums.stream()
  .explode((Album element, Consumer<Track> into) ->
into.accept(element.tracks))
  .collect(Collectors.<Track>toList());

On Thu, Jan 24, 2013 at 6:05 PM, Joe Bowbeer <joe.bowbeer at gmail.com> wrote:

> Kevin,
>
> To clarify this for me, how would Arul's sample look with your proposed
> change?
>
>
> https://github.com/aruld/java-oneliners/blob/master/src/main/java/com/aruld/oneliners/Item10.java
>
>
>
> // Merge tracks from all albums
> List<Track> allTracks = albums.stream()
>
>   .explode((Downstream<Track> downstream, Album element) -> downstream.send(element.tracks))
>
>   .collect(Collectors.<Track>toList());
>
>
>
> --Joe
>
>
> On Thu, Jan 24, 2013 at 5:36 PM, Kevin Bourrillion <kevinb at google.com>wrote:
>
>> On Thu, Jan 24, 2013 at 4:27 PM, Brian Goetz <brian.goetz at oracle.com>wrote:
>>
>> 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;
>>
>>
>> No, my point is that these methods are useful and natural and they belong
>> in Consumer/Whatever, regardless.  It's both convenient for the caller, and
>> gives implementors opportunities for greater efficiency. (I don't see how
>> this is full circle unless that exact idea was what was discussed, which it
>> doesn't sound like.)
>>
>>
>> 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.
>>>
>>
>> I would disagree with such a child interface, because the two are the
>> same thing.
>>
>>
>>
>>> 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.
>>>
>>
>> Okay, then why not leave that door open for all Consumers, as I propose?
>>
>>
>>
>>> 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?
>>
>>
>> No, I dislike both. :-)  And haven't yet heard any clear argument against
>> my suggestion.  I hope to hear others' opinions.
>>
>>
>>
>>>
>>> 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>
>>>>
>>>
>>
>>
>> --
>> Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
>>
>
>


-- 
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/lambda-libs-spec-experts/attachments/20130124/d6d4e3f4/attachment.html 


More information about the lambda-libs-spec-experts mailing list