explode

Remi Forax forax at univ-mlv.fr
Wed Feb 6 15:59:04 PST 2013


On 02/07/2013 12:30 AM, Brian Goetz wrote:
>> You said that we should not use Collection explicitly in the stream API
>> hence we don't have toList(), toSet(), or groupBy() but
>> collect(toList()), collect(toSet()) or  collect(groupingBy)
>> and at the same time, for flatMap which will be less used, you want to
>> add flatMapToCollection, flatMapToArray.
>
> Yes, any coupling to Collection is undesirable and has to be 
> justified.  We're currently in a nice place (zero uses of Collection 
> in Stream) so it would be nice to stay there, and one is a lot worse 
> than zero.
>
> But be careful that you try to turn consistency into a goal unto 
> itself.  For example, the use of Collections in Collectors is an ideal 
> compromise; the important thing is they are out of the core interface 
> which we expect every aggregate for the next 10+ years to implement, 
> but are still available for easy use through standalone static helper 
> methods like groupingBy.  This is an ideal balance of giving users 
> tools to do their job without tying Stream to Collection.
>
>> I think you should be at least consistent, so either we have an Exploder
>> like we have a Collector,
>> or we have several overloads for flatMap, groupBy and toList/toSet.
>
> Personally, I would (fairly strongly) prefer to have only:
>
>   Stream<U> flatMap(FlatMapper<T, U>)
>
> and
>
>   Stream<U> flatMap(Function<T, Stream<U>>)
>
> One can quite easily derive the Collection (and with slightly more 
> work, array) cases from the first form (or the second form, with more 
> runtime overhead):
>
>   .flatMap((t, sink) -> getColl(t).forEach(sink))
>   .flatMap(t -> getColl(t).stream())
>
> In fact, the first is what we originally had.  But then people howled 
> that (a) "I can't understand flatMap" and (b) "I think flatMap should 
> take a Function<T, Collection<U>>".  In our early focus groups, people 
> saw the base form of FlatMap and universally cried "WTF?"  People 
> can't understand it.  After 100 people make the same comment, you 
> start to get that its a pain point.
>
> So, the proposal I made today attempts to take into account that 
> people are not yet ready to understand this form of flatMap, and 
> attempts to compromise.  But I'll happily retreat from that, and vote 
> for just
>
>   Stream<U> flatMap(FlatMapper<T, U>)
>   Stream<U> flatMap(Function<T, Stream<U>>)
>
> It just seemed people weren't OK with that.  (Though to be fair, we 
> didn't always have the second form, and its addition might be enough 
> to avoid the need for the Collection and array forms.  It also allows 
> reclaiming of the good name "flatMap", since there is actual mapping 
> going on, and the generator form can piggyback on that.)
>
> So, +1 to Remi's implicit suggestion:
>
>   Stream<U> flatMap(FlatMapper<T, U>)
>   Stream<U> flatMap(Function<T, Stream<U>>)
>
> That's the new proposal.
>
> Will be carved in stone in 24h unless there is further discussion :)
>

I will vote on this if FlatMapper also defines static methods to see a 
function to a collection or to an array as a FlatMapper.

interface FlatMapper<T, U> {
   public void explodeInto(T t, Consumer<? super U> consumer);

   public static <T, U> FlatMapper<T, U> explodeCollection(Function<? 
super T, ? extends Collection<? extends U>> function) {
     return (element, consumer) -> 
function.apply(element).forEach(consumer);
   }
   ...
}

so one can write:
   stream.flatMap(explodeCollection(Person::getFriends))

Rémi




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