Vavr: Add Tuple support to Stream

Created on 8 Jul 2016  路  6Comments  路  Source: vavr-io/vavr

I use javaslang wherever possible, and I find myself using Tuples in streams to pass along multiple related objects.

What I do is usually

Stream<Tuple<String, SomeData>> stream = // previous steps
stream.map( tuple -> someFunction(tuple._1(), tuple._2()));

Whereas, with Tuple support in streams, I could do

stream.map( (name, data) -> someFunction(name, data));

In practice, map (and related, such as filter) should accept a Function<T1,T2, U> if the stream is of type Tuple<T1,T2> , and so on, and so on...

The improved clarity would be invaluable.

I'm sorry if this is a duplicate, I did query the issue tracker, but found nothing.

question 芦vavr-collection禄

Most helpful comment

Do you think such helper would be useful enough to generate together with tuples?

static <T1, T2, R> Function<Tuple2<T1, T2>, R> tuple2(Function2<T1, T2, R> mapper) {
    return tuple -> tuple.apply(mapper);
}
//Stream<Tuple<String, SomeData>> stream
stream.map(tuple2((name, data) -> "Hello " + name));
stream.map(tuple2(this::someFunction));

All 6 comments

Hi @civitz,

thank you for using Javaslang and also thank you for your suggestion. I really appreciate it!

Unfortunately Java's type system is not capable of differentiating the API based on the underlying generic type (the feature is also known as _higher order kinds_ or _higher kinded types_).

If Java could do it, we could implement flatten for example, which flattens a Stream<T> to Stream<U> if it is of type Stream<Stream<U>>.

Providing specializations for each type argument arity (read: Tuple arity) does not scale very well. Even if it were possible, I don't think that it is practicable from the viewpoint of API design.

In an ideal case, Java would have native support for Tuples with a certain degree of syntactic sugar, i.e. given a function someFunction(A, B) and a Stream<(A, B)> these would be equivalent:

stream.map(t -> someFunction(t));
stream.map((a, b) -> someFunction((a, b)));
stream.map((a, b) -> someFunction(a, b));

This would scale well from the perspective of API design.


The only things I can suggest to you are:

1) Simplify your existing code a bit by writing tuple._1 instead of tuple._1(). The use of the method tuple._1() makes only sense in the case of method references, i.e. tuple::_1.

2) Consider providing helper methods if the methods are frequently used as shown by you above. Example:

<T1, T2> U someFunction(Tuple2<T1, T2> tuple) {
    return someFunction(tuple._1, tuple._2);
}

<T1, T2> U someFunction(T1 t1, T2 t2) {
    ...
}

Chances are good that the Java compiler is smart enough to substitute the someFunction(Tuple2) calls with someFunction(T1, T2) calls (am I right @paplorinc?).

And then

stream.map(this::someFunction);

or

stream.map(SomeClass::someFunction);

I hope this may help you. I will close this ticket because we can't do anything here.

Thanks again!

Daniel

We could generate all the variants, but I agree, it's not really worth the effort.
You could probably create the helpers @danieldietrich recommended (it's what I did with the Java 8 map also, to allow (k, v) -> like lambdas, Groovy style.

About the inlining, I'm not sure, we could check it out by writing a simple JMH benchmark and running it with "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining" to see whether it's inlined or not.

Thx Lorinc!

Do you think such helper would be useful enough to generate together with tuples?

static <T1, T2, R> Function<Tuple2<T1, T2>, R> tuple2(Function2<T1, T2, R> mapper) {
    return tuple -> tuple.apply(mapper);
}
//Stream<Tuple<String, SomeData>> stream
stream.map(tuple2((name, data) -> "Hello " + name));
stream.map(tuple2(this::someFunction));

Updated - Sorry guys, I confused our _old_ lift with the current of. I'm not comfortable with having of1, of2, ... instead of of because of is used as standard name throughout the whole library (and Java itself).

@nbardiuk, @civitz Yes, great! It is already there:

stream.map(Function2.of(this::someFunction).tupled())

Your helper is more concise. If we had Function2.of2() instead of Function2.of(), we could write:

stream.map(of2(this::someFunction).tupled())

Maybe we should introduce this syntactic sugar for function _type_-lifting (and deprecate of?). Is anyone interested in implementing it?

But caution: _type-lifting_ is used in different contexts in function programming. E.g. there exist Monad lifting, Functor lifting (also known as _fmap_), etc. However, the Javaslang core lib only has function type-lifting at the moment.

One more word: This special naming would only make sense in the context of lifting for now. We will not rename all other methods (like map -> map2, map3, ...).

First of all: thank you all for the detailed reply.

Your proposed solutions (both @danieldietrich and @nbardiuk variants), are both great from the code clarity point of view. And I also agree on the effort for the generated code.

So, in the end, thank you for helping me, and for your time :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

roookeee picture roookeee  路  5Comments

manu-m picture manu-m  路  6Comments

enelson picture enelson  路  5Comments

HiDAl picture HiDAl  路  3Comments

paplorinc picture paplorinc  路  6Comments