Skip to content

Shorter constructor declarationsΒ #4485

@munificent

Description

@munificent

We're working on primary constructors. Among many nice things about this feature is that it eliminates the verbosity of repeating the entire class name when writing a constructor, which may sometimes be quite long:

// Today:
class SomeEmbarrassinglyLongClassNameWhy {
  SomeEmbarrassinglyLongClassNameWhy() {
    print('Phew!');
  }
}

// Using an in-body declaring constructor:
class SomeEmbarrassinglyLongClassNameWhy {
  this() {
    print('Phew!');
  }
}

This is nice, but since a class can only have one in-body declaring constructor and it must be generative, it's of limited help for classes with multiple constructors or factory constructors.

I think there is general sentiment on the language team to come up with a shorter notation for all constructor declarations. We've discussed it informally but I don't think there's a tracking issue. Now there is. :)

A couple of options:

Use this instead of the class name

Generalizing from in-body declaring constructors, we can use this for all constructors:

Current syntax                            Proposed
----------------------------------------- -------------------
LongClassName() {}                        this() {}
LongClassName.name() {}                   this.name() {}
const LongClassName() {}                  const this() {}
const LongClassName.name() {}             const this.name() {}
LongClassName(): this.other();            this(): this.other();
LongClassName.name(): this();             this.name(): this();
const LongClassName(): this.other();      const this(): this.other();
const LongClassName.name(): this();       const this.name(): this();
factory LongClassName() { ... }           factory this() { ... }
factory LongClassName.name() { ... }      factory this.name() { ... }
factory LongClassName() = D;              factory this() = D;
factory LongClassName.name() = D;         factory this.name() = D;
const factory LongClassName() = D;        const factory this() = D;
const factory LongClassName.name() = D;   const factory this.name() = D;

Literally just replace the class name with this in all constructors. This syntax makes generative constructors ambiguous with an in-body declaring constructor. I believe that's fine. An in-body declaring constructor with no field parameters is semantically identical to a generative constructor. So when the syntax is fully ambiguous, the semantics are the same so the distinction doesn't matter.

If a constructor has any field parameters, then their presence implies that the constructor is unambiguously a declaring one.

Put another away, with this syntax, we'd say that the definition of an in-body declaring constructor is a constructor that has at least one field parameter.

Use new instead of the class name

We could keep this for declaring/primary constructors and use new in place of the class name for all other constructors:

Current syntax                            Proposed
----------------------------------------- -------------------
LongClassName() {}                        new() {}
LongClassName.name() {}                   new.name() {}
const LongClassName() {}                  const new() {}
const LongClassName.name() {}             const new.name() {}
LongClassName(): this.other();            new(): this.other();
LongClassName.name(): this();             new.name(): this();
const LongClassName(): this.other();      const new(): this.other();
const LongClassName.name(): this();       const new.name(): this();
factory LongClassName() { ... }           factory new() { ... }
factory LongClassName.name() { ... }      factory new.name() { ... }
factory LongClassName() = D;              factory new() = D;
factory LongClassName.name() = D;         factory new.name() = D;
const factory LongClassName() = D;        const factory new() = D;
const factory LongClassName.name() = D;   const factory new.name() = D;

This has the advantage that you can recognize a declaring constructor at a glance since it will be the only one that starts with this.

It has the disadvantages of containing const new and factory new, which may send confusing conflicting signals to readers.

Use new with some other tweaks

If const new and factory new are confusing, we could tweak the syntax in some combinations to avoid those. Here's one way:

Current syntax                            Proposed
----------------------------------------- -------------------
LongClassName() {}                        new() {}
LongClassName.name() {}                   new.name() {}
const LongClassName() {}                  const() {}
const LongClassName.name() {}             const.name() {}
LongClassName(): this.other();            new(): this.other();
LongClassName.name(): this();             new.name(): this();
const LongClassName(): this.other();      const(): this.other();
const LongClassName.name(): this();       const.name(): this();
factory LongClassName() { ... }           factory() { ... }
factory LongClassName.name() { ... }      factory.name() { ... }
factory LongClassName() = D;              factory() = D;
factory LongClassName.name() = D;         factory.name() = D;
const factory LongClassName() = D;        const factory() = D;
const factory LongClassName.name() = D;   const factory.name() = D;

This simply removes new constructors that already have const or factory. It's the shortest of all three proposals.

It is technically breaking because we would have to treat factory more like a reserved word to do this. Currently, factory() { ... } is valid syntax for an instance method named factory.

Opinion

Personally, I lean towards the first proposal. I like the idea that it means this is uniformly the keyword for "produce a new instance" without stepping into the distinction between const/non-const and generative/factory. It makes moving a constructor from declaring to non-declaring a little more seamless because you don't have to touch a leading keyword.

Metadata

Metadata

Assignees

No one assigned

    Labels

    brevityA feature whose purpose is to enable concise syntax, typically expressible already in a longer formfeatureProposed language feature that solves one or more problemsprimary-constructorssmall-featureA small feature which is relatively cheap to implement.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions