Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Placement of type parameters in primary constructor syntax. #3082

Open
lrhn opened this issue May 17, 2023 · 1 comment
Open

Placement of type parameters in primary constructor syntax. #3082

lrhn opened this issue May 17, 2023 · 1 comment

Comments

@lrhn
Copy link
Member

lrhn commented May 17, 2023

The (most recently) proposed syntax for primary constructors puts type parameters after the name of a named constructor:

// Allow `C.name` to occur together.
class C.name<X extends List<Y>, ... /*lots of stuff*/>(Y y) {...}

This has the advantage that C.name is easily findable and readable, and the constructor name is not split from the class name by a potentially large type parameter list.

However, it doesn't match the call site of the constructor, where type parameters go on the class name, and more worrisome to me, it would conflict with having generic constructors (#647) as primary constructors, where you would want to put a second type parameter list at that position.

I suggest putting the type parameters on the class name, same place they occur in non-primary-constructor classes, and place the named constructor identifier with the parameter list:

// Follow the constructor invocations.
class C<X extends List<Y>, ... /*lots of stuff*/>.name(Y y) {...}

That way the logic is that:

class Name<TypeParams> extends/implements... {
  fieldsForArgs;
  Name.foo(args);
  ...
}

can be rewritten as:

class Name<TypeParams>.foo(args) extends/implements... {
  //                  ^^^^^^^^^^  -- inserted part
  ...
}

which moves the part of the constructor after Name into the class declaration line, just before any extends or implements clause.

If we put the name before the type parameters, and the arguments after, it's instead:

class Name.foo<TypeParams>(args) extends/implements... {
  //      ^^^^            ^^^^^^  -- inserted parts
  ...
}

Not a big difference, but still slightly more complicated.

Both options have readability issues, because primary constructors stuff a lot of information into very little syntax, with some of it being used for more than one thing.
Some of those issue are avoided by plain constructors by not having type parameters in the declaration at all (but #1899 would allow that).

@eernstg
Copy link
Member

eernstg commented May 17, 2023

Agreed! I tried out various choices, and ended up with class C.name<X extends B>(args) because it keeps C.name together (such that it can be read at a glance and searched), but it is certainly an unpleasant fact that it conflicts with the call sites for a generic constructor, and differs from a call site for a constructor where we're passing type arguments to the class.

Perhaps we should go even further and simply reject primary constructors on generic classes?

class C<X>.name(X x); // Best case, as proposed here. Really not so bad...

class C.name<X>(X x); // Current proposal, clashes with call `new C<int>.name(2)`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants