Draft JEP: Derived Record Creation (Preview)

forax at univ-mlv.fr forax at univ-mlv.fr
Wed Jan 24 21:43:18 UTC 2024


> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "Remi Forax" <forax at univ-mlv.fr>, "Gavin Bierman" <gavin.bierman at oracle.com>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.org>
> Sent: Wednesday, January 24, 2024 9:33:34 PM
> Subject: Re: Draft JEP: Derived Record Creation (Preview)

>> And as a general remarks, I hope there will be a following JEP about record
>> instance creation that allows to use the syntax of a transformation block to
>> initialize a record.
>> Because as this have been already discussed on several mailing list, if we only
>> give the derived instance creation syntax, people will twist it to be able to
>> initialize a record by component names, by adding an empty constructor that
>> does nothing on the record. Defeating the idea that constructors should ensure
>> that an invalid instance is not possible to create.

> Two things about this.

> 1. In a sense, there _already is_ a way to create a record instance using the
> syntax of a transformation block: it is called a compact constructor. If you
> look carefully, the body of a compact constructor, and RHS of a
> with-expression, are the same thing -- they are blocks for which N mutable
> locals magically appear, the block gets run, the final values of those locals
> are observed, and fed to the canonical constructor of a record.

> But I know this is not what you mean.

> 2. You are hoping that this can be turned into something like invoking
> constructor parameters by name rather than positionally. But it seems that your
> argument here is not "because that would be a really good thing", but more
> "people want it so badly that they will distort their code to do it". But
> that's never a good reason to add a language feature.
I agree, but it may be a reasonable reason to *not* introduce a feature if the main way it is used teach people to avoid precondtions in record constructor. 

I do not hope anything, i'm not ones that write a record with a dozen fields for a living. But seeing how far people (and my students) are willing to go to have classes initialized by names, i.e. write a full builder class per record, add a dependency on an annotation processor like record-builder or lombok, etc, it's easy too see how this feature will be abused. 

Data classes usually: 
- can have a lot of components, 
- are updated because business requirement changes modify the data, 
- are application specific, so unlike methods of the JDK/libraries, it's hard to remember them. 
so having a way to create them by spelling each component by name is actually a good way to make the code readable. 

That's why people goes to a great length to use named parameters. 

And for the anecdote, a recurrent question of my students with a C background is to ask how to initialize a class with the field names like C 99 (*). 

> I think many of the "turn records into builders" proposals (of which there are
> many) leave out an important consideration: that the real value of by-name
> initialization is when you have an aggregate with a large number of components,
> most of which are optional. Initializing with

> new R(a: 1, b: 2, c: 3)

> is not materially better than

> new R(1, 2, 3)

> when R only has three components. It is when R has 26 components, 24 of which
> are optional, that makes things like:

> new R(a:1, z :26)

> more tempting. But the suggestion above doesn't move us towards having an answer
> for that, and having to write out

> new R(a: 1, b : <default-for-b>, c: <default-for-c>, ... z: 26)

> isn't much of an improvement.

> For records for which most parameters _do_ have reasonable defaults, then a
> slight modification of the trick you suggest actually works, and also captures
> useful semantics in the programming model:

> record R(int a /* required */,
> int b /* optional, default = 0 */,
> ...
> int z / * required */) {

> public R(int a, int z) { this(a, 0, 0, ..., z); }
> }

> and you can construct an R with

> new R(1, 26) with { h = 8; };

> where the alternate constructor takes the required parameters and fills in
> defaults for the rest, and then you can use withers from there. (People will
> complain "but then you are creating a record twice, think of the cost", to
> which the rejoinder is "then use a value record.")

It is nice but in a way it does not solve the problem fully because people may still want to initialize the required parameters of R with named parameters. 

Rémi 
(*) The same way my students with a Python background (all my student nowadays, because in France, Python is now mandatory in highschool) ask how to create a tuple in Java. 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-experts/attachments/20240124/0c981aa8/attachment-0001.htm>


More information about the amber-spec-experts mailing list