Possible records tweak

Brian Goetz brian.goetz at oracle.com
Fri Apr 24 18:24:39 UTC 2020

I have been reviewing various Stack Overflow and other queries regarding 
the use of records, and in particular the compact constructor.

The compact constructor was intended to support a model where the 
constructor body minimally mutates the _constructor arguments_ (if they 
need to be normalized, defaulted, or clamped), which are then 
automatically committed to the fields on successful exit:

     record Rational(int num, int denom) {
         Rational {
             int gcd = gcd(num, denom);
             num /= gcd;
             denom /= gcd;

However, developers do not seem to be getting this, and they are instead 
appealing to the old idiom:

     record Rational(int num, int denom) {
         Rational {
             int gcd = gcd(num, denom);
             this.num = num / gcd;
             this.denom = denom / gcd;

My concern here is severalfold:

  - Mixing the "user initialized" and "auto initialized" idioms is 
potentially confusing, as some fields
    will be DA at some points in the constructor body, and others 
won't.  This invites confusion both
    for writers and readers.

  - There will be "style wars" over which idiom is better; since this is 
a new feature and was designed
    with a particular idiom in mind, we may simply prefer to enforce 
that idiom.

  - My intent is to carry this idiom elsewhere into the language, at the 
appropriate point in time.
    While for records, we have an implicit mapping of ctor args to 
fields that enables this auto-
    init feature, it may be useful to also allow for an explicit mapping 
in non-record cases.
    This might look like this (please, let's not design this feature 
now, I share this as a
    "where we might want to go" thought as it affects the feature 
currently on the table):

     class Foo(int x, int y) {
         int x;
         int y;

         Foo(int __this.x, int __this.y) { }

Here, we are annotating the constructor argument in a way that says "the 
name and type correspondence between this constructor argument and the 
field of the same name is not accidental, please fill in the boilerplate 
for me", and we would get the same auto-init benefit as we do with 
compact constructors in records.

(Also (and please, we are not designing this one now either), at some 
point we will have declared deconstruction patterns, which may look very 
much like "reverse constructors", and will be candidates for the same 
"this binding variable corresponds to a field" treatment, with the same 
potential for boilerplate elimination.)

So there's room in the future for the user to fill in the "this 
similarity is not an accident" and be rewarded with more compact, less 
error-prone code just like we do with compact constructors.

So, the question I want to ask is: should we simply _ban_ reads and 
writes to `this.x` in the body of a compact constructor, and let the 
auto mechanism take care of it, so there is no confusion about mixing 
initialization modes, the correct idiom, or reading possibly 
uninitialized fields? (If we did, we would probabably extend this to 
`this`-bound fields in the future, should that feature come to pass.)

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20200424/406e6fbb/attachment.htm>

More information about the amber-spec-experts mailing list