flatMap (original) (raw)

Brian Goetz brian.goetz at oracle.com
Tue Dec 18 21:03:02 PST 2012


Pushing a strawman implementation that uses the following API:

public interface MultiFunction<T,U> { public void apply(Collector collector, T element);

 /** A collector for values associated with a given input.  Values 

can be * yielded individually, or in aggregates such as collections, arrays, or * streams; aggregates are flattened, so that yielding an array containing * [1, 2] is equivalent to yield(1); yield(2). */ public interface Collector { void yield(U element);

     default void yield(Collection<U> collection) {
         for (U u : collection)
             yield(u);
     }

     default void yield(U[] array) {
         for (U u : array)
             yield(u);
     }

     default void yield(Stream<U> stream) {
         stream.forEach(this::yield);
     }
 }

}

interface Stream { Stream mapMulti(MultiFunction<? super T, R> mapper); }

Probably not all the way there, but I think definitely better than what we've got now.

Comments welcome.

On 12/17/2012 10:18 PM, Brian Goetz wrote:

So, of the names suggested here so far for flatMap, my favorite is the one inspired by Don -- mapMulti. It still sounds like map, is pretty clear what it's about (multi-valued map), and it steers clear of a lot of other pitfalls.

While the bikeshed paint is still wet, we can talk about the API. Here's an improved proposal. This may not be perfect, but it's definitely better than what we have now. interface DownstreamContext /* placeholder name */ { void yield(T element); void yield(T[] array); void yield(Collection collection); void yield(Stream stream); // can add more } interface Multimapper<T,U> /* placeholder name */ { void map(DownstreamContext downstream, T element); } interface Stream { ... Stream mapMulti(Multimapper<T,U> mapper); ... }

This handles the "generator" case that the current API is built around, but also handles the other cases well too: Example 1 -- collection. foos.mapMulti((downstream, foo) -> downstream.yield(getBars(foo)))... Example 2 -- generator. ints.mapMulti((d, i) -> { for (int j=0; j<i; j++)_ _d.yield(j);_ _})_ _Example 3 -- stream._ _kids.mapMulti(_ _(d, k) -> d.yield(adults.stream().filter(a -> isParent(a, f)))); The downstream context argument is still annoying, but I think is clearer than the current "sink" argument is. The alternative would be to have N special-purpose functional interfaces and N overloads for the non-generator cases (stream, collection) in addition to the current generator form.



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