Skip to content

Commit 4cb6a22

Browse files
committed
WIP
1 parent dbacff9 commit 4cb6a22

File tree

1 file changed

+51
-41
lines changed

1 file changed

+51
-41
lines changed

working/4213-generic-constructors/feature-specification.md

+51-41
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ extends Comparable<X>`. This implies that current Dart constructors can
110110
only allow for a declaration that resembles `D.ofComparable` if it uses a
111111
much less precise typing, and relies on some run-time type checks.
112112

113-
114113
```dart
115114
// What we can do in current Dart.
116115
@@ -157,7 +156,7 @@ The extra constraints can be helpful during inference. For instance,
157156
`Map.keyToList(xs)` above would yield a `Map<int, List<int>>`. In contrast:
158157

159158
```dart
160-
// What we can do in current Dart doesn't work...
159+
// What we may try to do in current Dart doesn't work...
161160
162161
class Map<K, V> {
163162
Map();
@@ -166,34 +165,31 @@ class Map<K, V> {
166165
}
167166
```
168167

169-
It is possible to create a `Map<K, List<K>>` based on an existing
170-
`Iterable<K>` using run-time type information. However, Dart does not in
171-
general support this kind of introspection into the run-time value of type
172-
arguments (and Dart does not (yet) have an 'existential open' operation),
173-
so we'd almost always have to use very general types in a way which is
174-
similar to the example above, which is a very substantial reduction in
175-
typing precision.
176-
177-
However, that won't work because it is a compile-time error to return the
178-
specified map literal in the declaration of `Map.keyToList`. So we'd need
179-
to cast it to `Map<K, V>`, and that's going to throw in an invocation like
180-
`Map<int, String>.keyToList(xs)`.
181-
182-
With the generic constructor, the actual type arguments like
183-
`<int, String>` will be used as a context type for the constructor
184-
invocation. The generic constructor `Map.keyToList` fails to infer actual
185-
type arguments such that the resulting return type is a subtype of
186-
`Map<int, String>`, and hence an invocation of the generic constructor like
187-
`Map<int, String>.keyToList(xs)` will be a compile-time error.
188-
189-
It should be noted that the type arguments of the class are inaccessible in
168+
However, that is a compile-time error because it returns a
169+
`Map<Object?, List<Object?>>` where the return type is `Map<K, V>`.
170+
But we don't know `K` or `V`, and we can't assume that `V` is of the form
171+
`List<K>` or a supertype thereof. We might try to cast the map literal to
172+
`Map<K, V>`, and that might work, but an invocation like
173+
`Map<int, String>.keyToList(xs)` will then throw at run time because the
174+
map literal isn't going to have the required type no matter which iterable
175+
we are passing as `keys`.
176+
177+
With the generic constructor and with an invocation like
178+
`Map<int, String>.keyToList(xs)`, the actual type arguments will be used as
179+
a context type for the constructor invocation. The generic constructor
180+
`Map.keyToList` fails to infer actual type arguments such that the
181+
resulting return type is a subtype of `Map<int, String>`, and hence the
182+
invocation is a compile-time error.
183+
184+
It should be noted that the type parameters of the class are inaccessible in
190185
a generic constructor declaration. In that sense, the generic constructor
191186
declaration is similar to a static member declaration, in that it can
192187
declare and use its own formal type parameters, but it cannot access the
193188
type parameters from the enclosing class.
194189

195190
The similarity to generic static methods goes further. For example, we
196-
could express `keyToList` as a static method in current Dart as follows:
191+
could express `keyToList` as a generic static method in current Dart as
192+
follows:
197193

198194
```dart
199195
// Emulating `keyToList` as a static method in current Dart.
@@ -210,9 +206,11 @@ void main() {
210206
}
211207
```
212208

209+
Works perfectly!
210+
213211
This illustrates that the new expressive power is not new for static
214212
members (and the invocations can look exactly the same as a constructor
215-
invocation in many cases).
213+
invocation in many cases), it is only new for constructors.
216214

217215
However, it is still useful to generalize constructors in this way because
218216
certain situations require the use of a constructor rather than a static
@@ -233,23 +231,30 @@ The grammar is adjusted as follows:
233231
| <typeIdentifier> <typeArguments> '.' <identifierOrNew>
234232
<formalParameterList>
235233
236-
<factoryConstructorSignature> ::= 'const'? 'factory' <constructorSignature>
234+
<factoryConstructorSignature> ::=
235+
'const'? 'factory' <constructorSignature>
237236
238237
<redirectingFactoryConstructorSignature> ::=
239-
'const'? 'factory' <constructorSignature> '=' <constructorDesignation>
238+
'const'? 'factory' <constructorSignature> '='
239+
<constructorDesignation>
240240
241-
<constantConstructorSignature> ::= 'const' <constructorSignature>
241+
<constantConstructorSignature> ::=
242+
'const' <constructorSignature>
242243
```
243244

245+
A _type introducing_ declaration is a class declaration, a mixin
246+
class declaration, a mixin declaration, an enum declaration, or an
247+
extension type declaration.
248+
244249
A compile-time error occurs if the `<typeIdentifier>` in the constructor
245-
signature is not the same as the name of the enclosing class, mixin or enum
250+
signature is not the same as the name of the enclosing type introducing
246251
declaration, or the name of the on-declaration of the enclosing extension
247252
declaration.
248253

249254
### Static Analysis
250255

251-
A generic constructor declaration occurs as a member of a class, mixin,
252-
mixin class, enum, or extension declaration. Its current scope is the body
256+
A generic constructor declaration occurs as a member of a type introducing
257+
declaration or an extension declaration. Its current scope is the body
253258
scope of the enclosing declaration. It introduces a type parameter scope
254259
whose enclosing scope is the current scope of the generic constructor
255260
declaration, and each type parameter declaration introduces that type
@@ -258,29 +263,29 @@ for the entire generic constructor declaration. Further scopes inside the
258263
type parameter scope are created in the same way as for non-generic
259264
constructors.
260265

261-
*In particular, a parameter of the form `this.p` is in scope in the
266+
*For example, a parameter of the form `this.p` is in scope in the
262267
initializer list, if any, and other parameters are in scope in the body, as
263268
usual.*
264269

265270
We establish some coherence conditions for generic constructors:
266271

267272
A compile-time error occurs if any identifier in a generic constructor
268273
declaration resolves to a type parameter which is declared by the enclosing
269-
class, mixin, enum, or extension declaration.
274+
type introducing declaration or extension declaration.
270275

271276
*In other words, a generic constructor cannot access the type parameters of
272-
the class directly. In this way they are similar to static members.*
277+
a class etc. directly. In this way they are similar to static members.*
273278

274279
Assume that _D_ is a generic constructor declaration whose constructor
275280
signature includes a list of actual type arguments which are applied to the
276281
`<typeIdentifier>`.
277282

278283
*For example, `C<X>.name<X extends num>()` applies `C` to `<X>`.*
279284

280-
It is a compile-time error if the enclosing class, mixin, or enum
281-
declaration or the on-declaration of the enclosing extension declaration
282-
does not declare any type parameters, or if it declares a different number
283-
of type parameters than the number of type arguments which are passed.
285+
It is a compile-time error if the enclosing type introducing declaration,
286+
or the on-declaration of the enclosing extension declaration, does not
287+
declare any type parameters, or if it declares a different number of type
288+
parameters than the number of type arguments which are passed.
284289

285290
It is a compile-time error unless these type arguments satisfy the declared
286291
bounds, assuming that the bounds of the generic constructor declaration
@@ -312,7 +317,12 @@ non-generic class).
312317
In this case, the super-initializer of the constructor (explicit or
313318
implicit, and excepting `Object` that does not have a super-initializer)
314319
will invoke the superconstructor with actual type arguments that correspond
315-
to the type `C<T1 .. Tk>` of the current constructor invocation.
320+
to the type `C<T1 .. Tk>` of the current constructor invocation.
321+
322+
That is, if `C` is declared with `k` type parameters `X1 .. Xk` and
323+
superclass `B<U1 .. Us>` then the `j`th actual type argument to the super
324+
constructor invocation is obtained as `[T1/X1 .. Tk/Xk]Uj`, for `j` in
325+
`1 .. s`.
316326

317327
Moreover, in the body of _D_, the reserved word `this` has static type
318328
`C<T1 .. Tk>`.
@@ -360,8 +370,8 @@ whose constructor signature applies a list of actual type arguments to the
360370
where `k` is zero, which again implies that `C` is a non-generic class).
361371

362372
In this case, the denoted redirectee constructor is invoked with the same
363-
actual type arguments. It is a compile-time error if the redirectee is a
364-
generic constructor.
373+
actual type arguments, that is `T1 .. Tk`. It is a compile-time error if
374+
the redirectee is a generic constructor.
365375

366376
*This restriction might also be relaxed in the future, if needed.*
367377

0 commit comments

Comments
 (0)