Skip to content

Commit 8924565

Browse files
authored
Add more examples and elaborations. Fix some typos.
1 parent 441c3d1 commit 8924565

File tree

1 file changed

+60
-17
lines changed
  • working/0698 - Enhanced Default Constructors

1 file changed

+60
-17
lines changed

working/0698 - Enhanced Default Constructors/proposal.md

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Dart Enhanced Default Constructors
22

3-
4-
Version: 0.3 (draft)
3+
4+
Version: 0.4 (draft)
55

66
## Background
77

@@ -115,7 +115,18 @@ An initializing constructor for a class *C* is a generative constructor with *im
115115

116116
A generative constructor can be made initializing by writing `default` as a modifier before the constructor, after any `const` modifier.
117117

118-
Such a constructor must not declare any optional positional parameters. It may declare other parameters, and then an initializing formal is not introduced for an instance variable when the constructor declares another parameter with the same name. This allows users to explicitly initialize some fields and still have default initialization for the remaining fields. The constructor may have an initializer list and a body as normal, the `default` modifier implicitly introduces a number of named initializing formal parameters into parameter list, and it does nothing else.
118+
An initializing constructor *must not* declare any optional positional parameters. It may declare other parameters, and then an initializing formal is not introduced for an instance variable when the constructor declares another parameter with the same name. This allows users to explicitly initialize some fields and still have default initialization for the remaining fields. The constructor may have an initializer list and a body as normal, the `default` modifier implicitly introduces a number of named initializing formal parameters into parameter list, and it does nothing else.
119+
120+
In short, an initializing constructor is implicitly expanded as follows:
121+
122+
* For each instance variable declared by the class which
123+
* has a non-private name `x`,
124+
* does not have an initializer expression at the declaration,
125+
* does not have an initializer in the initializer list of the constructor,
126+
* is not declared `late`,
127+
* and where there is not a declared constructor parameter with the same name,
128+
* a named parameter of the form `this.x` is added to the constructor.
129+
* If the variable's type is potentially non-nullable, the parameter is instead `required this.x`.
119130

120131
#### Example
121132

@@ -158,16 +169,18 @@ The superconstructor entry is expanded to add a number of *forwarded parameters*
158169
- If the forwarding constructor is also an initializing constructor, then those named field-initializing parameters are added to the forwarding constructor before considering forwarding.
159170
- If the forwarding constructor declares a parameter (including initializing constructor parameters introduced above) with the same name as a required named superconstructor parameter, it is a *compile-time error*.
160171
- If the forwarding constructor declares a parameter (including initializing constructor parameters introduced above) with the same name as an optional named superconstructor parameter, then the superconstructor parameter is ignored and no corresponding parameter is introduced in the forwarding constructor
161-
- If the forwarding constructor declares a parameter (including initializing constructor parameters introduced above) with the same name as a positional superconstructor parameter, call it *originalName*, then, for documentation purposes, the corresponding forwarding constructor parameter uses the name *originalName_n* where *n* is a decimal integer representing for the smallest integer greater than zero which makes the name different from any parameter name declared by the forwarding constructor or by the superconstructor.
162-
- If the forwarding constructor declares any named parameters, including if it is an initializing constructor, then all optional positional superconstructor parameters are ignored and no corresponding parameter is introduced in the forwarding constructor..
172+
- If the forwarding constructor declares a parameter (including initializing constructor parameters introduced above) with the same name as a positional superconstructor parameter, call it *originalName*, then, for documentation purposes, the corresponding forwarding constructor parameter uses the name *originalName$n* where *n* is a decimal integer representing the smallest integer greater than zero which makes the name different from any parameter name declared by the forwarding constructor or by the superconstructor.
173+
- If the forwarding constructor declares any named parameters, including if it is an initializing constructor, then all optional positional superconstructor parameters are ignored and no corresponding parameter is introduced in the forwarding constructor.
163174
- If the forwarding constructor declares any positional parameters after the superconstructor reference entry, then all optional positional superconstructor parameters are ignored and no corresponding parameter is introduced in the forwarding constructor.
164175
- Otherwise the forwarded parameters are optional if they are optional in the superconstructor, and if so, they have the same default value, if any.
165176

177+
In short, we forward all superconstructor parameters that can meaningfully be added to the subclass constructor, omit those which can't, and make it an error if any of those which can't are required parameters.
178+
166179
Then a super-invocation is added to the initializer list where each such forwarded parameter is forwarded as a corresponding superconstructor argument.
167180

168-
The constructor may have an initializer list and a body as normal, excapt that the initializer list must not contain an explicit superconstructor invocation.
181+
The constructor may have an initializer list and a body as normal, except that the initializer list must not contain an explicit superconstructor invocation.
169182

170-
#### Example
183+
#### Examples
171184

172185
The class `ShippableParcel`above could be given an initializing constructor by writing:
173186

@@ -195,24 +208,42 @@ class Color3DPoint extends Point {
195208
final Color color;
196209
final int z;
197210
final double distanceFromOrigo;
198-
// Expands to: Color3DPoint(this.color, int x, int y, this.z) : super(x, y);
211+
// Expands to:
212+
// Color3DPoint(this.color, int x, int y, this.z)
213+
// : distanceFromOrigo = sqrt(x * x + y * y + z * z),
214+
// super(x, y);
199215
Color3DPoint(this.color, super, this.z)
200216
: distanceFromOrigo = sqrt(x * x + y * y + z * z); // Cache the value.
201217
}
202218
```
203219

220+
Forwarding copies everything about the parameter, including default value.
221+
222+
```dart
223+
class Complex {
224+
final num r, i;
225+
Complex(this.r, [this.i = 0]);
226+
}
227+
228+
class ColorComplex {
229+
final Color color;
230+
// Expands to:
231+
// ColorComplex(this.color, num r, [num i = 0]) : super(r, i);
232+
ColorComplex(this.color, super);
233+
}
234+
```
235+
204236
### Default Constructors
205237

206238
A default constructor is a constructor which is automatically inserted if a class declares *no* constructors.
207239

208-
Let *C* be a class declaration with name `C` and superclass *S* with name `S`, and which declares no constructors.
209-
210-
It is a compile-time error if:
240+
Let *C* be a class declaration with the name `C`, a superclass *S* with the name `S`, and which declares no constructors.
211241

212-
- *C* declares a private-named, non-`late`, and potentially non-nullable instance variable with no initializer expressions.
213-
- Or in **NNBD-legacy mode**, *C* declares a private-named and final instance variable with no initializer expression.
242+
It is a *compile-time error* if:
214243

215-
It is also a *compile-time error* if *S* does not declare an unnamed generative constructor.
244+
- *C* declares a private-named, non-`late`, and potentially non-nullable instance variable with no initializer expressions. _(Such a variable needs an initializer, and since the name is private, we cannot introduce a named parameter to initialize it)._
245+
- In **NNBD-legacy mode**, *C* declares a private-named and final instance variable with no initializer expression. _(Same, but for legacy code)._
246+
- If *S* does not declare an unnamed generative constructor.
216247

217248
Otherwise let *s* be the unnamed generative constructor of *S*.
218249

@@ -248,15 +279,15 @@ Superclass constructors with optional positional parameters are hard to forward
248279

249280
You only get a default constructor when you declare *no* constructors. At least it's easy to introduce the equivalent of a default constructor explicitly: `default Foo(super);`.
250281

251-
You only get the unnnamed constructor as default forwarded constructor. We could forward all accessible named constructors too, but it might introduce constructors that you are not interested in, or not aware of, but which your clients start using anyway. If you later add a static member with the same name, it will hide the named forwarding constructor and break client code. It's easy to get a forwarding constructor if you do want it: `default Foo.foo(super.foo);`.
282+
You only get the unnamed constructor as default forwarded constructor. We could forward all accessible named constructors too, but it might introduce constructors that you are not interested in, or not aware of, but which your clients start using anyway. If you later add a static member with the same name, it will hide the named forwarding constructor and break client code. It's easy to get a forwarding constructor if you do want it: `default Foo.foo(super.foo);`.
252283

253284
It's always possible to write exactly the same constructors manually, so this change does not introduce any new expressive power. It's always possible to move away from default constructors to explicitly written constructors (except for classes used as mixins, which are always going to be highly restricted). As such, the feature has no back-end impact, it can be implemented entirely in the front-end. Back-ends may want to tree-shake unused constructors, or unused constructors parameters, though.
254285

255286
## Consequences
256287

257288
The change is *non-breaking* for default constructors. Any existing valid class that gets a default constructor with the new specification would also get a default constructor in the existing language. It will have a superclass with an unnamed constructor accepting zero arguments, and it will have no fields requiring initialization, so it too will have an unnamed constructor accepting zero arguments. Calling that constructor with no arguments will initialize any instance variable with no initializer to `null` and call the superclass unnamed constructor with the same result as calling it with zero arguments. That is exactly the same behavior as currently defined for the default constructor.
258289

259-
The change is non-breaking for mixin application because the semantics of forwarding constructors with no extra parameters declared matches the existing behavior for forwarding mixin application construtors.
290+
The change is non-breaking for mixin application because the semantics of forwarding constructors with no extra parameters declared matches the existing behavior for forwarding mixin application constructors.
260291

261292
The added features, initializing constructors, forwarding constructors and initializing and forwarding default constructors, do introduce new ways to cause errors. We have attempted to minimize such cases, but whenever there is an implicit connection between two classes, a change to one may change the other without anybody meaning to.
262293

@@ -280,15 +311,27 @@ This means that we *can* perhaps choose to do *less* and still be useful.
280311

281312
We may also want to allow forwarding constructors declared on mixins, which currently do not allow constructors. That would allow a significant number of use-cases.
282313

283-
We may want to allow non-nullable instance variables to be declared on mixins and still provide forwarding constructors, or allow final instance variables on mixins while still having a const forwading and initializing constructor.
314+
We may want to allow non-nullable instance variables to be declared on mixins and still provide forwarding constructors, or allow final instance variables on mixins while still having a const forwarding and initializing constructor.
284315

285316

286317
## Summary
287318

288319
Default constructors now have named initializing formal parameters for each public instance variable declared in the class, unless the variable is late or has an initializer. The parameter is required if the variable is potentially non-nullable (a parameter has to be required when its type is potentially non-nullable).
289320

321+
Example:
322+
323+
```dart
324+
class Point {
325+
final int x;
326+
final int y;
327+
// Implicit default Point(super);
328+
// Expands to: Point({required this.x, required this.y}) : super();
329+
}
330+
```
331+
290332
Default constructors forward parameters to an unnamed superclass generative constructor where possible, not just to the unnamed zero-argument superclass constructor. Forwarding is not possible when the superclass constructor has a required named argument which is shadowed by a parameter of the subclass constructor (including the ones introduced to initialize fields).
291333

292334
This works for all classes that do not declare any constructor. Mixin applications get all constructors forwarded, which matches current behavior.
293335

294336
The initializing and forwarding constructor features are available for explicit use, not only as part of default constructors.
337+

0 commit comments

Comments
 (0)