Platinum Partner
java,functions,tuples,functional programing,performance

What's Wrong in Java 8, Part V: Tuples

In part 5 of our "What's Wrong in Java 8" series, we turn to Tuples.

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

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}