-
Notifications
You must be signed in to change notification settings - Fork 205
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
Shorter constructor declaration syntax. #3788
Comments
maybe I'm asking a silly one, but how does this interact with #2364? |
Currently, some ways of declaring constructors reuse method declaration syntax. Consider: class ??? {
Foo() {}
} If the class is called Foo, then Foo is a constructor declaration. If the class is not called Foo, then I think it would be great if constructors and methods were syntactically distinct so that we can definitively say whether a constructor is a constructor and a method a method without one having to consider context further up the parse tree. It seems like this proposal would be able to make that happen since const/new/factory aren't valid declaration names and the dot syntax is unambiguous with method declaration syntax. |
Please consider an alternative: final class NativeFloat64x2List {
final Float64List _storage;
constructor new(int length) : _storage = NativeFloat64List(length * 2);
constructor _externalStorage(this._storage);
constructor _slowFromList(List<Float64x2> list) : _storage = NativeFloat64List(list.length * 2) {/* ... */}
factory fromList(List<Float64x2> list) {/*...*/ }
} WHY? static interface NativeFloat64x2ListStaticInterface {
constructor new(int length);
constructor _externalStorage(Float64List storage); // private ones won't be here,
constructor _slowFromList(List<Float64x2> list); // but imagine they were included - for illustration
factory fromList(List<Float64x2> list);
} If NativeFloat64x2 had a static method - say, static interface NativeFloat64x2ListStaticInterface {
constructor new(int length);
// etc. - as before
int foo(int x);
} What does it buy us? static interface Copyable<T> {
T copy(T t);
}
class User implements static Copyable<User> {
final String name;
User(this.name);
static User copy(User u)=>User(u.name);
} When we say static interface UserStaticInterface {
constructor new(this.name);
User copy(User u)=>User(u.name);
} If |
Would be great to make |
Putting undue emphasis on "factory" is unfair to generative constructors. They deserve more respect! :-) Let's view the issue from another angle. What change would be the easiest to explain? final class NativeFloat64x2List {
final Float64List _storage;
constructor (int length) : _storage = NativeFloat64List(length * 2); // no "new"
constructor _externalStorage(this._storage);
constructor _slowFromList(List<Float64x2> list) : _storage = NativeFloat64List(list.length * 2) {/* ... */}
factory fromList(List<Float64x2> list) {/*...*/ }
} It works well with |
My main complaint about What if it was So try the above with final class NativeFloat64x2List {
final Float64List _storage;
new(int length) : _storage = NativeFloat64List(length * 2); // no "new"
new _externalStorage(this._storage);
new _slowFromList(List<Float64x2> list) : _storage = NativeFloat64List(list.length * 2) {/* ... */}
factory fromList(List<Float64x2> list) {/*...*/ }
} That's basically what I proposed, just without a So why is that const name(args); Works too. May step on a later constant functions feature. |
"constructor", as long a word as it is, is still shorter than a typical class name. Real class names in flutter (and elsewhere) tend to be rather long because they (understandably) have to concatenate several words, like "AlwaysScrollableScrollPhysics". Replacing a name like this with a shorter "constructor" is still a considerable gain. "constructor" has one thing going for it: it's very visible among other declarations. Today, the constructors are visible due to the repetition of the class name. Class names follow a very recognizable syntactic pattern, which makes them "stand out". "new" is less visible IMO. According to Wikipedia, the standard abbreviation for "constructor" is "ctor", but there's no example of the language introducing a "ctor" keyword. The term "constructor" is used in javascript and kotlin - look up "secondary constructor". (Some languages indeed use the name "new", others follow a traditional C++/java format). Since the constructors are not declared very often, using a bit longer, but more explicit, keyword seems appropriate. You can always set up IDE so that "ctor" will be expanded into "constructor". Elsewhere, "Grammarly" plugin will fix a wrong spelling. I thought the issue is somehow entangled with the concept of static interfaces (#356), where the word "constructor" would look more readable to me than the alternatives, though this is subjective. (I don't know whether you have plans to include constructors in the static interface, but this would be highly desirable: the ability to say When the feature is considered in isolation, everyone will vote for a shorter syntax, but here, the feature is interrelated with a number of other features that have to be included in the context. EDIT: speaking of "const", I find these two declarations inconsistent with each other: const.name(this.x);
const factory.name(int x) ... One The rules of migration are complicated (what has to be replaced by what, what has to be dropped, insert dot here, but not there :-). I tried to formulate all the rules, came up with a decision tree, which I won't be able to internalize :-) Another issue. Suppose the class has a number of static methods (foo, bar) etc. static interface XInterface {
new(String); // it was const(...) in the class! we have to drop "constness" by replacing "const" with "new"
new.name(int);
factory.new(int);.
factory.name(int x)
foo(int);
bar(int);
} My point is that the constructors should be featured more prominently, or else it's hard to tell them from static methods. Thinking more about it, maybe |
My two cents: I wouldn't like a new keyword. Instead, the already defined |
Not sure if I like this. Makes more exceptions and doesn't bring any value |
I actually really don't like this proposal. I get it, but it reads too weirdly, and costs us readability. There is too much difference with how we normally do it. Treating const and factory as functions feels... Really bad. That's not what they are, but it's what the semantics imply. Worse, factory.new and const.new implies that they're objects which is even worse. Why don't we just... Use Just straight up remove the type name, and do something like this: class Foo {
final int x;
.(this.x);
// (this.x); // thoughts?
// .new(this.x); // explicit
const .(this.x);
factory .(int x) {...}
const factory .(int x) = SubFoo;
const .zero() : x = 0;
factory .parse(String source) {...}
} It retains the existing syntax, and makes it pretty obvious that it's distinct from normal methods. All of the benifit, none of the... Weirdness. I would have no f'n clue what the hell a factory.new or a const() is supposed to be Far too much cognitive overhead with the proposal as it is. Consider this alternative? In other words, remove the type identifier (and add a dot for the unnamed one, or otherwise allow for us to use new as a "named" constructor for this) |
A constructor declaration repeats the class name.
That's not really necessary. A constructor needs some syntax to define it as a constructor, but repeating the (potentially quite long) class name is noisy and verbose.
The declaration syntax mimicks how the constructor is called, so
Foo(int x)
is called asFoo(42)
, but that's not an argument we use for instance or static members. (And it wasn't true originally, not until Dart madenew
optional.)So really it's because "Java and C# did it", the two languages Dart was designed to be familiar to users of. And both probably took it from C++, which did it because then they didn't need to introduce another keyword.
We can do better.
Proposal
Instead of writing the class name when declaring a constructor, use the word
new
, or for aconst
constructor, useconst
instead of the name and theconst
prefix.The affected grammar rules are:
The result of these rules is to allow the following declarations:
Basically, for a generative constructor, write
new
instead of the class name, orconst
instead of the name and a leadingconst
.For factory constructors use the
factory
instead of the class name, except for the non-constant unnamed constructor, which needs to befactory.new
.It needs that because
factory
is currently not a reserved word, andfactory(int x) => 42;
is currently a valid method declaration. There are probably other workarounds, possibly makingfactory
at least a contextual keyword, but this is short and doesn't require much new syntax. (Suggestions welcome. We do risk conflicts withconst factory(int x) => ...
in the future if we ever start allowing "constant methods" or class-level expressions or other weird stuff.)Making
factory
a reserved word, or evne just a contextually reserved word at the start of a class-level declaration, is breaking.Notice that this grammar does not allow combinations like
const.new(...)
andnew.new(...)
. Because that looks ridiculous.A declaration containing a
const
is a constant constructor declaration, a declaration containingfactory
is a factory constructor declaration, and the part after the signature still determines whether it's a redirecting constructor or not.In every case, the currently allowed constructor signature can be recreated by inserting the class name into the declaration at the correct place, or by replacing a leading
new
. That makes it easy to understand, it's like a "class name inference" that infers the name of the constructor.Examples
A declaration of
becomes
And with more constructors:
Summary
This change allows omitting the class name from some constructor declarations, in two cases inserting the keyword
new
instead to have something to recognize the constructor by.I am not absolutely sure that makes code more readable.
The capitalized word of a current constructor stands out.
But standing out and being verbose may be correlated.
Maybe this is too little, and we should have a completely different constructor syntax, instead of trying to tweak the existing (C++ legacy) syntax.
Or maybe we don't need any change, what we have "works" even if it's occasionally annoying to write, because it is actually easy to read, and familiar to almost everybody.
The text was updated successfully, but these errors were encountered: