A proposal (pre-proposal?) to add parameter groups to Java

Behrang Saeedzadeh behrangsa at gmail.com
Thu Jul 30 04:36:21 UTC 2020


> This request is in a category that I like to call "please, can we have
bad ad-hoc tuples."  ("Multiple return" is another one in this category.)
 Which is to say, while it seems like it makes things better, it just moves
the problem around.

Every discussion has to start from somewhere... ¯\_(ツ)_/¯

> If you declare a method
>
>     void line((int x, int y) p1) { ... }
>
> what is the type of P1?  What can I do with it, besides extract x and y?
Can I return one?  Can I declare a local variable of that type?  (Your
proposal suggests no to all of these, as you only mentioned method
parameter grouping.)
>
> Which is to say, you are creating a new kind of structured literal `(a,
b)`, but which _only_ can be used as a parameter to a method that wants one
of these.  You can see how this would be limited.  The trouble with such
"easy" proposals is that they help you move forward one step, and then are
stuck in the exact same way as before, just one step farther.
>
> What you really want is structural tuples, where for any types T and U,
`(T, U)` is a type which is an ordered pair of a `T` and `U`, and you'd be
able to use this as a return type, parameter type, local variable type,
array component type, type bound for generics, etc.  But, that's a much
bigger thing than what you are asking.  Relatively few languages have
succeed at mixing nominal and structural types very effectively (Java's nod
to structural types is arrays, and there are visible seams where array
types meet the rest of the type system.)
>
> Many languages (e.g, Haskell, ML) do have structural tuples.  Such
languages tend to integrate pattern matching into the language, so that
destructuring is built into how we declare functions.  For example, in
Haskell, I can define:
>
>     type IntInt = (Int, Int)
>
> which says IntInt is a tuple of Int and Int, and then declare a function
like your line:
>
>     line :: IntInt -> IntInt -> ()
>
> (which means "line is a function that takes two IntInt and returns void")
and then I can declare a definition for line:
>
>     line p q = ...
>
> where each of p and q are of type IntInt.  Alternately, I can define line
with a destructuring pattern match:
>
>     line (x1, y1) (x2, y2) = ...
>
> These two declarations are equivalent, since the type of `line` is that
it takes two (int, int) pairs; the first binds p and q to pairs, and the
latter matches structurally on the pairs and binds x1, y1, x2, y2 to the
coordinates.  (I could do the same without having declared IntInt; it's
just the equivalent of `typedef` in C.)
>
> So yes, there is precedent for such a thing -- but in languages that are
pretty different from Java.
>
> What we do in the Java world, instead, is lean on _nominality_.  Rather
than structural types launching into being by simply uttering their
structure, we declare them.  Java's nominal function types are functional
interfaces; Java's nominal tuples are records.
>
> So you could declare your method as:
>
>     record Point(int x, int y) { }
>
>     void line(Point p1, Point p2) { ... }
>
> Within line(), you can (eventually) use pattern matching to destructure
the point:
>
>     void line(Point p1, Point p2) {
>         Point(var x1, var y1) = p1;  // destructure p1 with Point
deconstructor into x1, y1
>         Point(var x2, var y2) = p2;
>     }
>
> (when we have destructuring patterns, which will come to records
eventually.)  You might even be able to put the destructuring in the
declaration (no promises!):
>
>    void line(Point(var x1, var y1) p1,
>              Point(var x2, var y2) p2) { }
> and then all of {x,y,p}{1,2} would be bound in the body.  This is a more
general solution, since Point is an ordinary class type, so can be used
anywhere.

This would cover my use case too, if we could declare and instantiate
anonymous records:

    void line((int x, int y) p1, (int x, int y) p2) {
        System.out.println(p1.x);
        System.out.println(p1.y);
        System.out.println(p2.x);
        System.out.println(p2.y);
    }

    line((0, 0), (10, 10));

Here `p1` and `p2` are anonymous records/tuples that have two `int` fields
`x` and `y`.

P.S: Is this mailing list the correct channel for us mere mortals to
discuss topics like this?


More information about the discuss mailing list