dart-lang / language

Design of the Dart language
Other
2.68k stars 205 forks source link

Primary constructor type parameter syntax #3172

Open munificent opened 1 year ago

munificent commented 1 year ago

The proposed syntax for a generic class with a named primary constructor (#3023) is:

class D.named<T>(int x);

It feels to me like it should be:

class D<T>.named(int x);

The type parameters are a property of the class itself, not the constructor. The syntax for invoking a named constructor on a generic class is:

D<String>.named(123)

Also, if we ever want to support generic constructors, we may find ourselves wanting to allow:

class D<ClassTypeParam>.named<CtorTypeParam>(int x);
lrhn commented 1 year ago

It should be class D<T>.named(int x);!

Cat-sushi commented 1 year ago

I feel current specification reasonable. because,

munificent commented 1 year ago
  • In case of class D(int x);, D is the name of constructor, but not that of the class.

It's the name of the class, at least according to how the spec reasons about this. The constructor doesn't have a name in this case. (If it were the name of the constructor, then D would be a constructor tear-off, but it isn't. It's a type literal for the class D.)

  • Type parameters of functions should be just after the name, but not in the middle of the parts of that.

But the type parameters are not on the function, they are on the class.

  • Constructors doesn't have type parameters separated from those of the class.

Not currently, but this a long-running feature request: #647.

  • Unnamed primary constructor couldn't have type parameters separated from those of the class, anyway.

They can. We anticipated that when adding constructor tear-offs by letting you write .new for the unnamed constructor. So if you wanted to have a generic unnamed constructor, you could define it like:

class C {
  C.new<T>() { ... }
}
eernstg commented 1 year ago

This was a dilemma.

As proposed, the constructor name is shown (so it's readable and searchable). If the class is generic then the type parameter list needs to go somewhere, and I put it after the complete constructor name. Of course, this conflicts with the constructor invocation syntax, but it is arguably similar to the current constructor declaration syntax (for those, there is no type parameter list, so we never have anything between the class name and the other half of the name of a named constructor).

We could also simply make it a syntax error to have both a type parameter list and a named primary constructor at the same time.

This means that the primary constructor will also have to be the "nameless" one in a generic class. In return, we won't ever have this funny configuration where the constructor name has a (perhaps long) type parameter list in the middle.

Another possibility is to say that the type parameter list goes in the same position as in the constructor invocations, and we don't care that a constructor named C.name is shown as C<Lots, Of, Stuff>.name in the declaration.

I don't have a strong opinion here.

munificent commented 1 year ago

Another possibility is to say that the type parameter list goes in the same position as in the constructor invocations, and we don't care that a constructor named C.name is shown as C<Lots, Of, Stuff>.name in the declaration.

This is what I prefer, definitely. Users are always apparently entirely comfortable reading code like List<SomeLongType>.filled(value), so I don't think there's anything wrong with the class declaration syntax being similar.

Cat-sushi commented 1 year ago

Ok, now I agree with @munificent ,

lrhn commented 1 year ago

I'm strongly for Name<TypeArgs>.identifier(args), and strongly against Name.identifier<TypeArgs>(args).

There are pragmatic reasons too, like being prepared for generic constructors, but mainly the latter just looks like a mistake. It doesn't match anything in the language, except the time we mistakenly allowed you to write List.filled<int>(...) as well as List<int>.filled(...). That was a parser bug, and we fixed it.

NUXI

eernstg commented 1 year ago

Cool, let's do Name<TypeArgs>.identifier(args)!