Performance Zone is brought to you in partnership with:

Pierre-Yves Saumont has managed his own company for 15 years, specializing in natural language processing. At the same time, he has been the publishing manager for the French subsidiary of a leading American computer books publisher. He wrote about 30 books about computers and software development in Java. Since 2008, he works for Alcatel-Lucent Submarine Networks as an R&D Software engineer, and he is responsible for the architecture of several projects such as distributed application framework, functional framework and functional DSML (Domain Specific Modeling Language). He is also a double bass Jazz player. Pierre-yves has posted 10 posts at DZone. You can read more from them at their website. View Full User Profile

What's wrong in Java 8, part V: Tuples

06.10.2014
| 34938 views |
  • submit to reddit

The first time a programmer is missing tuples is often when he feels the need to return multiple values from a method. As we all know, Java methods may take as many arguments as needed, but they can only return a single value. With most functional programming languages, this problem is somewhat different. Functions may only take one argument and return one value, but this argument and this value may be tuples.

What are tuples

At first sight, tuples looks like ordered collections of values of different types. Unlike arrays or lists, which may contain several values of the same type, tuples may “contain” values of mixed types. Most often used tuples are generally given specific names: a tuple of two elements is generally called a pair (or double, couple, dual, twin) and a tuple of three elements is generally called a triple. There are also such names as quadruple, quintuple and so on. Note that there are also tuples of 0 and 1 element. A tuple of one element is rarely used as such, since the element itself may be used instead. However, for theoretical discussion, it may be useful to consider such tuples. A tuple of one element is sometimes called a single. The Optional class in Java 8, is in fact a tuple of one element. It is sometimes easier to use the name tuplen with n being the number of elements.

In programming languages, tuples are generally noted with parenthesis:

(3, 5)

is a tuple composed of two integer values and:

(“age”, 42)

is a tuple composed of one string an one integer.

Does Java has tuples

Without any doubt, Java has tuples. It has implicit and explicit ones. Implicit tuples are the argument of what we called multi-argument methods:

int mult(int x, int y) {
  return x * y;
}

int z = mult(3, 5);

In this example (3, 5) may be seen as a tuple, which in turn make the mult method appear as a single argument one!

This is important if we think about functions. A function is an application of one set (the function's domain) into another set (the function's codomain).

It is important to note that the domain is ONE set. It can't be two sets, nor three sets, nor anything else. It may however be a multidimensional set, that is a set of tuples! So, a function may take only one argument.

Having functions of two or more arguments is only syntactic sugar for functions of pairs, triples, and so on. And what looks like a function of two arguments, as the following lambda in Java 8:

(int x, int y) -> x * y

is in fact a function form int x int (a Cartesian product) to int. And the result of the Cartesian product int x int is a single set of tuples.

So it is clear that Java has tuples, but we can use them only as function's arguments and not for function's return values.

Note: function's of two arguments are not always syntactic sugar for functions of tuples. They may also be used for functions of one argument to functions. This is related to arguments evaluation. For more details, see the first article in this series: What's Wrong in Java 8, Part I: Currying vs Closures

Creating our own tuples

Should we need to return tuples, it is however very easy to create some. For example, if we need to create a function from double to (int, double) taking a double as argument and returning its integer and decimal parts, we can write:

public class TupleExample {

  public static class Tuple {

    public final int integerPart;
    public final double decimalPart;

    public Tuple(int integerPart, double decimalPart) {
      super();
      this.integerPart = integerPart;
      this.decimalPart = decimalPart;
    }

    @Override
    public String toString() {
      return String.format("(%s, %s)", integerPart, decimalPart);
    }
  }

  private static Tuple split(Double x) {
    int integerPart = x.intValue();
    return new Tuple(integerPart, x - integerPart);
  };

  public static void main(String[] args) {
    System.out.println(split(5.3));
    System.out.println(split(-2.7));
  }
}

This program will display:

(5, 0.2999999999999998)
(-2, -0.7000000000000002)

Now, we see that most objects in Java are, in fact tuples!

Of course, it would be simpler to create a generic class for tuples, which is as easy (some methods such as equal and hashcode have been omitted):

public class TupleGenericExample {

  private static Tuple<Integer, Double> split(Double x) {
    int integerPart = x.intValue();
    return new Tuple<>(integerPart, x - integerPart);
  };

  public static void main(String[] args) {
    System.out.println(split(5.3));
    System.out.println(split(-2.7));
  }
}

public static class Tuple<T, U> {

  public final T _1;
  public final U _2;

  public Tuple(T arg1, U arg2) {
    super();
    this._1 = arg1;
    this._2 = arg2;
  }

  @Override
  public String toString() {
    return String.format("(%s, %s)", _1, _2);
  }
}

So, once again, since it is so easy to implement tuples, what is the problem? There are many problems:

  • We can write our own tuples, but we can't use them in a public API. To do so, we should have all Java developers to agree upon a common Tuple implementation. We can't do this, unless a Tuple implementation is added to the Java API. Of course, we would probably need Tuple3, Tuple4 and so on for small numbers of elements. (Although it is possible to define Tuple of more than two elements in terms of pairs, such as Tuple<T, Tuple<U, V>>, using structures like linked lists or binary trees, it would not be very practical.)

  • We can't check the type of a tuple with instanceof. So, if we want to check if an object is an instance of tuple, we would have to do the check for the raw type, and then as many checks as there are elements in the tuple.

  • To make Tuple easy to use, we would need the same syntactic sugar offered for arguments in lambda notation, that is we would need literals.

  • And there is the ubiquitous problem of primitives. Dealing only with four primitive types (int, long, double, boolean) would give 25 different types of pairs, 125 different types of triples, and so on. We may rely upon auto boxing/unboxing for this. Much better, we would again need value types.

In the meantime, we can use our own tuples internally. We can create, the tuples for primitives we need, or, better, we can use the corresponding object types and rely on auto boxing/unboxing as long as performance is not an issue.

What's next?

In the next article, we will talk in more details about the differences between function of “several arguments” being in reality functions of tuples, and those being function of functions.

Previous articles

What's Wrong in Java 8, Part I: Currying vs Closures

What's Wrong in Java 8, Part II: Functions & Primitives

What's Wrong in Java 8, Part III: Streams & Parallel Streams

What's Wrong in Java 8, Part IV: Monads

Published at DZone with permission of its author, Pierre-yves Saumont.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Oliver Weiler replied on Tue, 2014/06/10 - 9:57am

The idiomatic way to return multiple values in Java is to use a List. I'm writing Java code for about 5 years and never felt the need for Tuples.

Pierre-yves Saumont replied on Tue, 2014/06/10 - 11:11am in response to: Oliver Weiler

 Lists may be used to return multiple values, but:

- Number of elements may not be bound

- All elements must be of the same type or

- Elements type may not be checked

- List are mutable

- Lists have to be created the hard way (no literals)

So, although it may be idiomatic for you, it is surely not a very convenient idiom. (And I doubt a majority of Java developers are using this trick. But who knows...)

Greg Brown replied on Tue, 2014/06/10 - 1:07pm in response to: Oliver Weiler

Agreed. I've been using Java for 15+ years and have never felt the need for tuples.

To me, a tuple just seems like an untyped class, which doesn't seem very useful.


Andy Jefferson replied on Tue, 2014/06/10 - 1:42pm in response to: Oliver Weiler

Caller has to know the position of the objects (of whatever type - not type safe) in the List that is returned. That is not part of any type contract when hacking it with List, whereas with a Tuple it would be. 

What happens if the maintainer of some method decides to change the order or type of an element ? somebody has to do a search for all use of that method and check all usage. And with a Tuple, the compiler can do most of the work for them.

Valery Silaev replied on Tue, 2014/06/10 - 5:01pm in response to: Andy Jefferson

Andy,

Same way as with the list indexes, a tuple field names have no business meaning, i.e. it's the very same "anonymous" contract whether you have to refer result[3] or result._3

The only thing that tuples add is field-specific type information - but it's not the rare case when you have sevaral fields of the same type in a tuple, so a user of the method has to check always sources of the method or docs to understand what goes where.

Frankly, this is exactly the reason why I can't recall when I miss tuples, while regular typed POJO with either mutable or immutable (final) instance fields are more self-documented constructs.

John Piersol replied on Tue, 2014/06/10 - 5:56pm

I don't need tuples to pass around as much as multiple return values and simple creation of case classes that would be a better tuple with named parameters.

Example multiple return values (not really tuple since returned values are separate vars)

public (int, int) swap(int x, int y) { return (y, x); }
// Using in code.
(x, y) = swap(x, y);

Example case class (like scala or dart or some other syntactic sugar like Lombok):

// Auto generation of equals, toString , getters, ect...

class MyTuple(int a, double b);
public MyTuple getMyMethod() {
...
return new MyTuple(1, 2.0);
}
MyTuple t = someInstance.getMyTuple();
assert(t.a == 1);
assert(t.b == 2.0);

Simple. That would cover the use case for me without having to change the jvm or add any complexity. Multiple return types could just be some syntatic suger for returning an Object[] just like ... notation in method param list is.

Andy Jefferson replied on Wed, 2014/06/11 - 1:37am in response to: Valery Silaev

My point is not about using Tuples as such, but against using a List for such a return type (like in the example in the post). A List doesn't necessarily fit any "business meaning" either. The *only* time you should return a List is when the whole point of the method is to return a List, not to bundle unrelated values together - aka disaster waiting to happen.

What do people do right now ? They create a (often inner) class and return that, so retain type-safe handling. They have the overhead of creating a class, rather than the convenience of some syntactic solution. In many situations returning an instance of some class would make perfect sense (because the 'returned fields' fit together into some context), and the number of situations where the 'returned fields' don't fit together, likely suggest that the method structure needs redesigning/splitting to fit the business process better.

High use-case for Tuples ? no. it's a convenience, nice-to-have.


Igor Rodriguez replied on Wed, 2014/06/11 - 2:24am in response to: Oliver Weiler

I thought the idiom in Java was to create a POJO with the values. I really wouldn't like to invoke a function that returns a list with a number, a string and a custom type. In such a case you are losing type checking and may find a runtime error in the future.

Pierre-yves Saumont replied on Wed, 2014/06/11 - 2:27am

I agree with all developers who don't miss tuples. If you were not missing functions before Java 8, either you were not missing tuples, or you had created your own functions and tuples and all necessary stuff.

The problem is if you wan't to use Java in a more functional way (as advertised by Oracle). In such a case, you will probably need to create AND SHARE functions with more that one argument if not with more than one return value.

Java 8 offers “functions” of zero, one an two arguments returning zero or one value. What if you want to use a “function” of three arguments? This should be either a function returning a function returning a function of one argument (curried version) or a function of a tuple3 (since there is no such thing as a function of three arguments).

It is possible to define a Function3 interface using three arguments:

public static interface Function3<T, U, V, R> {
  R apply(T t, U u, V v);
}

It is also possible to define a Tuple3:

public static class Tuple3<T, U, V> {
  public final T _1;
  public final U _2;
  public final V _3;

  public Tuple3(T t, U u, V v) {
    this._1 = t;
    this._2 = u;
    this._3 = v;
  }
}

And it is possible to use either of the three possibilities to instantiate a “function” of three arguments:

// Custom functional interface
Function3<Integer, Integer, Integer, Integer> functionA = (x, y, z) -> x / y * z;

// Currying
Function<Integer, Function<Integer, Function<Integer, Integer>>> functionB = x -> y -> z -> x / y * z;

// Custom Tuple3
Function<Tuple3<Integer, Integer, Integer>, Integer> functionC = x -> x._1 / x._2 * x._3;

The problem is that we should not be using our own types if we want to be able to share them. So which types should have been included in standard Java8? There are obvious reasons not to include Function3 since, as I said, there is no such thing as a function of three arguments. Including Tuple3 (and other tuples) in the Java 8 API would have allowed us using and, most importantly SHARING such functions.

But, of course, we may use currying and we will not need Tuples for arguments. This is my favorite solution, but I don't think many Java developers would chose this. And we will still need them for return values.

NOTE: for simplicity, I used three parameters of the same type, but of course, tuples are even much more useful if types are different.

Werner Keil replied on Fri, 2014/06/13 - 7:43am

At least for a case like 

(“age”, 42)

and similar quantities, you may be delighted to hear, JSR 363 (Unitsofmeasurement.github.io) is going to provide a typesafe and extendable approach that checks whether "age" is of the right type compared to let's say "mass" or "length".


At the moment the main target is Java ME 8 and IoT, but just see the GitHub project we also got a SE 8 "upward port" if you want using the new features in Java 8.

Werner Keil
Co Spec Lead, JSR 363

Pierre-yves Saumont replied on Fri, 2014/06/13 - 9:41am in response to: Werner Keil

Hi Werner,

Adding specific classes to represent quantities is certainly a good thing because it will allow developers to share APIs using these types for arguments and return values. However, we will still face the problem of performance. In order for special types for quantities to be widely adopted, what we need is value types, i.e. classes representing values with no (or very little) performance penalty. We would need for example to be able to define (or use a predefined) Distance class and be able to write:

Distance a = new Distance(5, Distance.KM);

Distance c = a.map(x -> x * 2);

And this should perform (more of less) as fast as an int multiplication. And of course, Quantities should be immutable.

Ivano Pagano replied on Tue, 2014/06/17 - 3:25am in response to: Valery Silaev

There are common cases where tuples would be pretty useful, using conventional meanings, like

//map entries
Tuple2<KEY,VAL> entry;

//source/destination
Tuple2<SRC,DST> pipe;

//request/response
Tuple2<REQ,RES> communication;


This is just to say that I can personally think about many scenarios where I'd find it useful to use tuples (pairs in particular) and take advantage of both type-safety and conventional wisdom to identify the meaning of the tuple's elements.

This is clearly harder to apply to a public API, but I assume that the language is not targeted at library-builders only. Why should we give up the benefits of some shortcut in private code, especially when such code is shared only by a small team?

Bye

Ivano

Neale Something replied on Wed, 2014/06/18 - 8:54am

Anyone interested in progress on this might find John Rose's work on Value Types interesting:

There's also an earlier post about Tuples in the VM

For someone who has developed databases in Java, the key benefit for me is to be able to write code that is readable and meaningful *and* performant.  Value Types as return values (i.e. Tuples the JVM can optimise) give that benefit.

Marnix Van Bochove replied on Wed, 2014/06/18 - 4:46pm

Is it possible to use the interface Map.Entry and it's implementations as Tuple? It's part of java so can be used in a public API.

Pierre-yves Saumont replied on Wed, 2014/06/18 - 5:18pm in response to: Marnix Van Bochove

Map.Entry is an interface. So we could implement it and use it in a public API. It looks a bit odd, but it works.

Kris Nuttycombe replied on Sat, 2014/06/28 - 10:29am

I really wanted to just go away and forget that I'd seen this, but I can't let it go.

A tuple of one element is sometimes called a single. The Optional class in Java 8, is in fact a tuple of one element. 

This is wrong.

A tuple of one element is a value, and consequently has no sensible interpretation distinct from a bare value. Optional is something different. You should think about programming with values wrapped inside of Optional containers like you think about programming with values wrapped inside of lists.

When you program with values contained in lists, you write handling for the case where the list is empty, and the case where the list has n elements. The same is true of Optional; however, instead of 0 and n, you write handling for the cases of 0 and 1. With a tupleN, you *always* and *only* write handling for the case of n; with a tuple of one element, there is no 0 (no empty case) to write handling for.

Please, please correct this. It's a terrible misconception to associate product types (tuples) with sum types (Optional, List, enumerations, etc.)

Pierre-yves Saumont replied on Sat, 2014/06/28 - 6:34pm in response to: Kris Nuttycombe

> I really wanted to just go away and forget that I'd seen this...

You should probably have done so.

A tuple does not exist by itself. A tuple is only what we agree upon, for the need of the discussion. If you do not agree that a tuple2 is a type parameterized by two types, such a Tuple2<T, U>, it is your right, but you probably should "just go away and forget" , as you said.

On contrary, if you agree about this, you have to be consistent and agree that a tuple1 is a type parameterized by one type, such as Tuple1<T>.

I insist on this: if you agree with the first sentence and disagree with the second, we can't have any useful debate.

Optional<T>, as a type, is a tuple1. The Optional class in Java is also something else, but only because it defines some very specific methods which makes it a monad.

And no, I should not think about Optional as a container. List are not containers either. Lists are parameterized types. The "container" metaphor is only used for people who are not able to understand this concept.

Kris Nuttycombe replied on Sat, 2014/06/28 - 11:08pm in response to: Pierre-yves Saumont

A tuple does not exist by itself. A tuple is only what we agree upon, for the need of the discussion. If you do not agree that a tuple2 is a type parameterized by two types, such a Tuple2, it is your right, but you probably should "just go away and forget" , as you said.
This is absurd. So, is a function of one argument (which is a type parameterized by two types) a tuple? Is a sum type Either<A, B> with two inhabitants, Left<A, B> and Right<A, B> a tuple? Saying that these things are one another is not sensible. Nor is defining Optional<A> to be isomorphic to a tuple of one element.


Tuple2<A, B> is a type that has (m x n) inhabitants, where m is the number of possible inhabitants of type A and n is the number of possible inhabitants of type B. By comparison, Either<A, B> is inhabited by (m + n) types only, because a value of this type will only ever be a Left (inhabited by an A) or a Right (inhabited by a B) at any given time. 

Optional<A>, like Either, is a sum type, having at most (m + 1) inhabitants - the number of inhabitants of A, plus the empty case. A Tuple1<A> has no empty case, so it is isomorphic to A by itself.

Oh, and the fact that Optional has a monad has absolutely nothing whatsoever to do with this discussion. 

Pierre-yves Saumont replied on Sun, 2014/06/29 - 3:37pm in response to: Kris Nuttycombe

Optional<A>, like Either, is a sum type, having at most (m + 1) inhabitants - the number of inhabitants of A, plus the empty case. A Tuple1<A> has no empty case, so it is isomorphic to A by itself.

OK, you're right. Optional is not a Tuple1.

This is absurd. So, is a function of one argument (which is a type parameterized by two types) a tuple? Is a sum type Either<A, B> with two inhabitants, Left<A, B> and Right<A, B> a tuple? Saying that these things are one another is not sensible.

Of course it is absurd. But why are you saying this ? Did I say that all types parametrized by two types were Tuple2 ? Tuple2 is a type parameterized by two types. This does not mean that all types parameterized by two types are tuples.

Thank you anyway for pointing the error.  

Kris Nuttycombe replied on Sun, 2014/06/29 - 5:34pm in response to: Pierre-yves Saumont

Thank you very much for the correction.

With respect to your question, I was referring to this fragment of yours, which I quoted:

If you do not agree that a tuple2 is a type parameterized by two types, such a Tuple2, ...

I had a bit of trouble parsing this, and interpreted the last three words as "such as Tuple2," which must have not been your intended interpretation; I apologize for misunderstanding.

Sura Sos replied on Mon, 2014/07/07 - 9:42pm

Tuple is fixed size immutable container of ordered things.  

If you need to create  Tuple2 Tuple3 Tuple 4 Tuple5 classes to simulate tuple, it is better to stop and use list instead.

Kris Nuttycombe replied on Tue, 2014/07/08 - 10:11am in response to: Sura Sos

You're missing the point. A tuple is a heterogeneous record of a fixed number of elements, while a list is a homogeneous collection of an arbitrary number of elements.

That is to say, you cannot represent a Tuple3<Integer, String, Boolean> as a list in a typesafe fashion. However, there is a related construct which *can* be used instead of a fixed-arity tuple in languages with richer type systems: the HList. In Java, the closest you can get to this is to use an arbitrarily nested Tuple2; for example, the type above can be represented as Tuple2<Integer, Tuple2<String, Boolean>>. Not a very convenient type to work with, but at least it gives you arbitrary "arity" as well as type safety.

Sentient Meat Net replied on Sun, 2014/10/26 - 3:11pm in response to: Kris Nuttycombe

 Love the overall discussion and think tuples would greatly enhance Java, whether or not Java developers have "felt their lack" or not. Have the say the nested Tuple2<> types may be isomorphic to what we want, but they are really ugly.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.