(x,y) = getXY();
instead record Coord(int x, int y) {}
Coord coord = getXY();
// access as coord.x, coord.y
It's not terrible, but it's terribly javay. (x,y) = getXY();
instead record Coord(int x, int y) {}
Coord coord = getXY();
// access as coord.x, coord.y
It's not terrible, but it's terribly javay.Why not "just" do tuples?
Some readers will surely be thinking at this point: if we "just" had tuples, we wouldn't need records. And while tuples might offer a lighter-weight means to express some aggregates, the result is often inferior aggregates.
Classes and class members have meaningful names; tuples and tuple components do not. A central aspect of Java's philosophy is that names matter; a Person with properties firstName and lastName is clearer and safer than a tuple of String and String. Classes support state validation through their constructors; tuples do not. Some data aggregates (such as numeric ranges) have invariants that, if enforced by the constructor, can thereafter be relied upon; tuples do not offer this ability. Classes can have behavior that is derived from their state; co-locating state and derived behavior makes it more discoverable and easier to access.
For all these reasons, we don't want to abandon classes for modeling data; we just want to make modeling data with classes simpler. The major pain of using named classes for aggregates is the overhead of declaring them; if we can reduce this sufficiently, the temptation to reach for more weakly typed mechanisms is greatly reduced. (A good starting point for thinking about records is that they are nominal tuples.)
But the pain remains the same in client code. Because api's are written once, but used many times, it is more important to make client code simple than api code.
In mathematics and programming, tuple content is accessed by position, not by name. e.g. The arguments in method/constructor invocation form a tuple - so there is precedent, even within java itself. Note that callers (clients) address arguments by position, and callees address them by name (api's).
There'a a very similar passage in the article to the one you quoted. Regarding nominal vs structural there, a better example is that java's class system is nominal.
"records" are nominal in that they have a class-like name, and also their content is accessed by name. Tuples are structural (not named), and access by position (not name). So I think records are not really "tuples".
1. when assigning to a variable, type inference is easy, because can't subclass records, so can omit type ceremony. So, although nominal, client can treat as structural.
2. with standard IDE completion/lookup, user need't know or enter the exact accessor names. So, the assigned variable acts like a namespace.
This mightn't be perfect, because appropriate names for the client's usage can differ from what is appropriate from the api's perspectice. (with tuples, user can name to suit their purpose).
(lo, hi) = getLim();
It seems to me it would have to be something like: Lim lim = getLim();
lo = lim.minimum;
hi = lim.maximum
Pwrhaps records could define an ordering of their fields, so as to enable destructuring by position?Yeah, this in JS:
obj = {a: 1, b: 22, c: 333}
const {b, a} = obj
My original point was more that, even in languages that have both "proper" traditional tuple types and the parenthesised destructuring syntax, we're not obligated to use destructuring. i.e. writing the following is perfectly legitimate, if verbose: a_tup = function_returning_tuple()
x = first(a_tup)
y = second(a_tup) lim = getLim();
const {minimum: lo, maximum: hi} = lim;