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.)




Reply via email to