flatMap (original) (raw)

Remi Forax forax at univ-mlv.fr
Wed Dec 19 11:32:52 PST 2012


On 12/19/2012 06:03 AM, Brian Goetz wrote:

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 collection) { for (U u : collection) yield(u); } default void yield(U[] array) { for (U u : array) yield(u); } default void yield(Stream 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.

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 yieldInto(U element);

     default void yieldInto(Collection<? extends U> collection) {
         for (U u : collection)
             yieldInto(u);
     }

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

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

}

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

I think we should not use the name 'yield' because it can be a keyword added later if we introduce coroutine, generator, etc. Also, the interface Collector is too close to Destination, I think its should be the same one.

Rémi

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