> On Jun 19, 2018, at 9:11 AM, Zheka Kozlov <orionllm...@gmail.com> wrote: > > The function you propose is just a binary variant of mapping: > > Collector<T, ?, R> mapping( > Function<T, U> mapper, > Collector<U, ?, R> downstream); > > (omitted '? super' for readability) > > So, it is logical to use the name biMapping: > > Collector<T, ?, R> biMapping( > Function<T, U1> mapper1, > Function<T, U2> mapper2, > Collector<U1 ?, R1> downstream1, > Collector<U2 ?, R2> downstream2, > BiFunction<R1, R2, R> finisher);
+1 > > > 2018-06-19 7:38 GMT+07:00 John Rose <john.r.r...@oracle.com>: > >> On Jun 18, 2018, at 2:29 PM, Brian Goetz <brian.go...@oracle.com> wrote: >>> >>> "bisecting" sounds like it sends half the elements to one collector and >> half to the other … >> >> The main bisection or splitting operation that's relevant to a stream is >> what >> a spliterator does, so this is a concern. >> >> Nobody has mentioned "unzipping" yet; this is a term of art which applies >> to streams >> of tuples. The image of a zipper is relatively clear and unambiguous, and >> the tradition >> is pretty strong. https://en.wikipedia.org/wiki/ >> Convolution_(computer_science) >> >> The thing we are looking at differs in two ways from classic "unzipping": >> First, the >> two collectors themselves convert the same T elements to whatever internal >> value >> (T1, T2) is relevant. Second, we are looking at a new terminal operation >> (a collector) which >> consolidates the results from both of streams (a notional Stream<T1> and >> Stream<T2>, >> if you like), rather than delivering the streams as a pair of outputs. >> >> The classic "unzip" operation applies "fst" and "snd" (or some other >> conventional >> set of access functions) to each T-element of the input stream. Since we >> don't >> have a privileged 2-tuple type (like Pair<T1,T2>) in Java, the user would >> need >> to nominate those two functions explicitly, either by folding them into a >> "mapping" >> on each collector, or as a utility overloading like this: >> >> unzipping( >> Function<? super T, T1> f1, // defaults to identity >> Collector<? super T1, ?, R1> c1, >> Function<? super T, T2> f2, // defaults to identity >> Collector<? super T2, ?, R2> c2, >> BiFunction<? super R1, ? super R2, ? extends R> finisher) { >> return toBoth(mapping(f1, c1), mapping(f2, c2)); >> } >> >> >>> "tee" might be a candidate, though it doesn't follow the `ing >> convention. "teeing" sounds dumb. >> >> >> "tee" sounds asymmetrical. "diverting" or "detouring" are "*ing" words >> that might >> express asymmetrical disposition of derivative streams. >> >> An asymmetrical operation might be interesting if it could fork off a >> stream of >> its own. It would have to have a side-effecting void-producing terminal >> operation, >> so the main (undiverted) stream could continue to progress at the top >> level of >> the expression. >> >> interface Stream<T> { >> default Stream<T> diverting(Consumer<Stream<T>> tee) { … } >> } >> >> values.stream().diverting(s2->s2.forEach(System.out:: >> println)).filter(…).collect(…); >> >> Or (and this might be a sweet spot) a symmetric stream-tee operation could >> materialize two sibling streams and rejoin their results with a bifunction: >> >> class Collectors { >> static <R1, R2, R> Stream<T> unzipping( >> Function<? super Stream<T>, R1> f1, >> Function<? super Stream<T>, R2> f2, >> BiFunction<? super R1, ? super R2, ? extends R> finisher) >> { … } >> } >> >> values.stream().unzipping( >> s1->s1.forEach(System.out::println), >> s2->s2.filter(…).collect(…), >> (void1, r2)->r2 >> ); >> >> This would allow each "fork child" of the stream to continue to use the >> Stream API instead of the more restrictive Collector operators. >> >> Optimal code generation for forked/unzipped/teed streams would be tricky, >> requiring simultaneous loop control logic for each stream. >> To me that's a feature, not a bug, since hand-writing ad hoc >> simultaneous loops is a pain. >> >> My $0.02. >> >> — John