@@ -52,11 +52,26 @@ module.
52
52
53
53
## Capability controls on types
54
54
55
- Dart defaults to being maximally permissive. When you define a class, it can,
56
- unless prohibited by its own structure, be used as an interface, superclass, or
57
- mixin. This is useful for consumers of the class because they are given the
58
- flexibility to do with it as they will. This flexibility comes with some
59
- downsides:
55
+ There are three fundamental kinds of entities in Dart's semantics:
56
+
57
+ * A ** class** has a set of member declarations and a superclass (which may be
58
+ Object). You can use a class ** construct** new instances (if not abstract)
59
+ and/or you can ** extend** one as a superclass.
60
+
61
+ * An ** interface** is a set of member * signatures* .
62
+
63
+ * A ** mixin** is a set of member declarations. Unlike a class, a mixin does
64
+ * not* have a superclass. You have to apply the mixin to some concrete
65
+ superclass in order to get a class that you can construct.
66
+
67
+ Dart's syntax somewhat obscures this. There is no dedicated syntax for declaring
68
+ an interface. Until recently there was also no syntax for declaring a mixin.
69
+ Instead, a class declaration can, unless prohibited by its own structure, be
70
+ used as an interface, superclass, or mixin.
71
+
72
+ Inferring interfaces from classes and mixins a useful tool to avoid the
73
+ redundancy found in Java and C# code. It provides consumers of the class maximum
74
+ flexibility. But it comes at a cost:
60
75
61
76
* Since a class may be used as an interface, adding a method is potentially a
62
77
breaking change, even if the author never intended the class's interface to
@@ -81,39 +96,24 @@ downsides:
81
96
82
97
[ ex ] : https://github.com/dart-lang/language/blob/master/working/0546-patterns/patterns-feature-specification.md#exhaustiveness-and-reachability
83
98
84
- Because of these, users ask for control over the affordances a class provides
85
- ([ 349] [ ] , [ 704] [ ] , [ 987] [ ] ). Adding modules to the language is a good
86
- opportunity to add those.
87
-
88
- Note that the above reasons only come into play when * unknown code* works with
89
- a class. Thus these restrictions only apply to code using a class outside of
90
- the module where the class is declared. Inside the class's own module, you are
91
- free to use the class however you want. It's your class.
92
-
93
- There are four capabilities a class may expose to outside code:
99
+ Because of these, users ask for control over the affordances a declaration
100
+ provides ([ 349] [ ] , [ 704] [ ] , [ 987] [ ] ). Modules are a natural boundary for those
101
+ restrictions.
94
102
95
103
[ 349 ] : https://github.com/dart-lang/language/issues/349
96
104
[ 704 ] : https://github.com/dart-lang/language/issues/704
97
105
[ 987 ] : https://github.com/dart-lang/language/issues/987
98
106
99
- * ** Constructible.** – Whether a new instance can be created by calling one of
100
- its constructors.
107
+ Note that the above problems only come into play when * unknown code* works with
108
+ a type. Thus these restrictions only apply to code using a type outside of the
109
+ module where the type is declared. Inside the types's own module, you are free
110
+ to use types however you want. It's your code.
101
111
102
- * ** Extensible.** – Whether the class can be used as a superclass of another
103
- class in its ` extends ` clause.
112
+ ### Types of types and capabilities
104
113
105
- * ** Implementable.** – Whether the class exposes an interface that can be
106
- implemented.
107
-
108
- * ** Mix-in** – Whether the class defines a mixin can be mixed in to other
109
- classes.
110
-
111
- Ideally, a class would have full control over which of these capabilities it
112
- allows and all combinations would be expressible. Dart already supports a
113
- couple of combinations: an ` abstract class ` cannot be constructed, and a ` mixin `
114
- declaration cannot be constructed or extended.
115
-
116
- An analysis of Google's corpus shows these combinations are most common:
114
+ Restating the above, there are four affordances a type might offer:
115
+ ** construct** , ** extend** , ** implement** , and ** mix in** . An analysis of the
116
+ class declarations in Google's corpus shows these combinations are most common:
117
117
118
118
```
119
119
Construct 63.93% 6605
@@ -124,114 +124,126 @@ Construct + Implement 2.36% 244
124
124
Mixin 1.25% 129
125
125
```
126
126
127
- All other combinations are less than 1% * but do occur in practice* . The latter
128
- implies that we should support all 16 combinations. To keep declarations terse,
129
- the default behavior should reflect the most common combinations and modifiers
130
- should opt for less common choices. Given the numbers above, that means classes
131
- should default to constructible, but not extensible, implementable, or
132
- mixin-able ("miscible"?).
127
+ All other combinations are less than 1%. Every combination occurs in practice,
128
+ though the few examples of combinations involving mixins and classes seem to be
129
+ historical from the time before Dart's dedicated mixin syntax.
130
+
131
+ ## Mixins and classes
132
+
133
+ Combinations of classes and interfaces make sense. Likewise, it seems natural to
134
+ derive an interface from a mixin. But deriving both a class and a mixin from the
135
+ same declaration has proven to be confusing.
136
+
137
+ A mixin, by definition, has no superclass. In order to construct or extend
138
+ something, it must be a full-formed class with an inheritance chain all the way
139
+ up to Object. When you derive a mixin from a class today, Dart discards the
140
+ superclass and any inherited methods.
141
+
142
+ This is a continuing source of confusion for users, which is one reason we added
143
+ dedicated ` mixin ` syntax. Now that we have language versioning, we can complete
144
+ that transition. With this proposal, ** a class declaration no longer defines an
145
+ implicit mixin declaration.** The only way to create a mixin is using ` mixin ` .
133
146
134
147
### Syntax
135
148
136
- Following Dart's existing syntax, we use ` mixin ` to allow mixing-in and
137
- ` abstract ` to prevent constructing. Following Java and others, we use
138
- ` interface ` to allow implementing. Following Swift and Kotlin, we use ` open ` to
139
- allow subclassing.
149
+ We support all combinations of the above four capabilities, except for
150
+ combinations with Mixin + Construct or Mixin + Extend.
140
151
141
- Using all of those strictly as modifiers on ` class ` would lead to some awkard
142
- combinations (like ` abstract interface ` for what would just be ` interface ` in
143
- other languages), so the rules for combining them are a little more complex. In
144
- grammarese, the allowed combinations and modifier orders are:
152
+ Following Dart's existing syntax, we use ` class ` to define classes, ` mixin `
153
+ define mixins, and ` abstract ` to prevent constructing. Following Java and
154
+ others, we use ` interface ` (as a modifier here) to allow implementing. Following
155
+ Swift and Kotlin, we use ` open ` to allow subclassing.
156
+
157
+ The updated grammar is:
145
158
146
159
```
147
160
topLevelDeclaration ::=
148
- abstractClassDeclaration
149
- | classDeclaration
150
- | interfaceDeclaration
161
+ classDeclaration
151
162
| mixinDeclaration
152
163
// existing rules...
153
164
154
- abstractClassDeclaration ::= 'open'? 'abstract' 'class' // ...
155
- classDeclaration ::= 'open'? 'interface'? 'mixin'? 'class' // ...
156
- mixinDeclaration ::= 'open'? 'interface'? 'mixin' // ...
157
- interfaceDeclaration ::= 'interface' // ...
165
+ classDeclaration ::= 'open'? 'interface'? 'abstract'? 'class' identifier
166
+ typeParameters? superclass? interfaces?
167
+ '{' (metadata classMemberDeclaration)* '}'
168
+
169
+ mixinDeclaration ::= 'interface'? 'mixin' identifier typeParameters?
170
+ ('on' typeNotVoidList)? interfaces?
171
+ '{' (metadata classMemberDeclaration)* '}'
158
172
```
159
173
160
174
That yields these combinations:
161
175
162
176
```
163
- class // 63.93% Construct
164
- abstract class // 14.09% (none)
165
- interface // 9.77% Implement
166
- open abstract class // 6.47% Extend
167
- interface class // 2.36% Implement Construct
168
- mixin // 1.25% Mix-in
169
- open class // 0.86% Extend Construct
170
- open interface // 0.76% Implement Extend
171
- open interface class // 0.20% Implement Extend Construct
172
- open mixin // 0.14% Mix-in Extend
173
- interface mixin // 0.09% Mix-in Implement
174
- open interface mixin // 0.03% Mix-in Implement Extend
175
- mixin class // 0.02% Mix-in Construct
176
- open mixin class // 0.02% Mix-in Extend Construct
177
- interface mixin class // 0.01% Mix-in Implement Construct
178
- open interface mixin class // 0.00% Mix-in Implement Extend Construct
177
+ class // 63.93% Construct
178
+ abstract class // 14.09% (none)
179
+ interface abstract class // 9.77% Implement
180
+ open abstract class // 6.47% Extend
181
+ interface class // 2.36% Implement Construct
182
+ mixin // 1.25% Mix-in
183
+ open class // 0.86% Extend Construct
184
+ open interface abstract class // 0.76% Implement Extend
185
+ open interface class // 0.20% Implement Extend Construct
186
+ interface mixin // 0.09% Mix-in Implement
179
187
```
180
188
181
- Note that the names gradually get longer for less common combinations, so it
182
- seems this is roughly in line with keeping the common options terse.
189
+ Using ` abstract class ` to opt out of constructing is pretty verbose, especially
190
+ when combined with other modifiers. We could conceivably drop it from classes
191
+ that have ` interface ` :
183
192
184
- ### Static semantics
193
+ ```
194
+ interface abstract class -> interface
195
+ open interface abstract class -> open interface
196
+ ```
197
+
198
+ But it might be confusing that a type declared using only ` interface ` does in
199
+ fact define a class that may have concrete methods and even be constructed and
200
+ used as a class inside the module. Dart users today are already used to using
201
+ ` abstract class ` to declare interfaces, so this isn't too much of a stretch.
202
+ Also, this leaves ` interface ` available as potential future syntax for defining
203
+ a pure interface, if a need for such thing should arrive.
185
204
186
- There are four "kinds" of types: ` abstract class ` , ` class ` , ` interface ` , and
187
- ` mixin ` .
205
+ ### Static semantics
188
206
189
- The rules for using the type within its module are as permissive as possible
207
+ Within a module, despite all the new modifiers, the semantics are roughly the
208
+ same. The rules for using a type within its module are as permissive as possible
190
209
and are based on the structure of the type itself, as in current Dart:
191
210
192
- * It is a compile-time error to invoke a generative constructor of a type if
193
- the type defines or inherits any unimplemented abstract members. * You can
194
- directly construct anything internally if it wouldn't cause a problem to do
195
- so, even an interface or an abstract class. * ** TODO: Even a mixin? * *
211
+ * It is a compile-time error to invoke a generative constructor of a class if
212
+ the class defines or inherits any unimplemented abstract members. * You can
213
+ directly construct abstract classes internally if it wouldn't cause a
214
+ problem to do so. Mixins never have generative cosntructors. *
196
215
197
- * It is a compile-time error to extend a type that has at least one factory
198
- constructor and no generative constructors.
216
+ * It is a compile-time error to extend a class that has at least one factory
217
+ constructor and no generative constructors. It is a compile time error to
218
+ extend a mixin.
199
219
200
- * It is a compile-time error to mix in a type that explicitly declares a
201
- generative constructor or has a superclass other than ` Object ` .
220
+ * It is a compile-time error to mix in a class.
202
221
203
222
The rules for using types * outside* of their module are based on the
204
- capabilities the type explicitly provides :
223
+ capabilities the type explicitly permits :
205
224
206
- * It is a compile-time error to invoke a generative constructor of an abstract
207
- class, interface, or mixin outside of the module where the type is defined.
225
+ * It is a compile-time error to invoke a generative constructor of a class
226
+ marked ` abstract ` outside of the module where the class is defined.
208
227
209
- * It is a compile-time error for a type to appear in an ` extends ` clause
210
- outside of the module where the type is defined unless the type is marked
228
+ * It is a compile-time error for a class to appear in an ` extends ` clause
229
+ outside of the module where the class is defined unless the class is marked
211
230
` open ` .
212
231
213
232
* It is a compile-time error for a type to appear in an ` implements ` clause
214
- outside of the module where the type is defined unless the type is an
215
- interface or is marked ` interface ` .
233
+ outside of the module where the type is defined unless the type is marked
234
+ ` interface ` .
216
235
217
- * It is a compile-time error for a type to appear in a ` with ` clause outside
218
- of the module where the type is defined unless the type is a mixin or is
219
- marked ` mixin ` .
236
+ * It is a compile-time error for a class to appear in a ` with ` clause.
220
237
221
238
We also want to make sure the type structurally supports any capability it
222
239
claims to offer. This helps package maintainers catch mistakes where they
223
240
inadvertently break a capability that the type offers.
224
241
225
242
* It is a compile-time error if a non-abstract class contains an abstract
226
- method or inherits an abstract method no corresponding implementation.
243
+ method or inherits an abstract method with no corresponding implementation.
227
244
* Since a non-abstract class declares that code outside the module can
228
- construct it, this rule ensures that it is safe to do so.*
229
-
230
- * All other kinds of types -- abstract classes, interfaces, and mixins -- may
231
- contain both abstract and non-abstract members. Even interfaces can contain
232
- non-abstract members. This is because while an interface can't be
233
- constructed or extended outside of the module, it can be internally if it
234
- has no abstract members.*
245
+ construct it, this rule ensures that it is safe to do so. Abstract classes
246
+ and mixins may contain both abstract and non-abstract members.*
235
247
236
248
* It is a compile-time error if a public-named type marked ` class ` does not
237
249
have a public-named constructor. * The constructor can be a default or
@@ -244,13 +256,10 @@ inadvertently break a capability that the type offers.
244
256
245
257
** TODO: Is this too much of a restriction?**
246
258
247
- * It is a compile-time error if a public-named type marked ` mixin ` defines
248
- any constructors. ** TODO: Is this restriction correct?**
249
-
250
259
* It is a compile-time error if a class C marked ` interface ` has a superclass
251
260
D which is not also marked ` interface ` , unless C and D are declared in the
252
- same module. * In other words, someone can't extend a class with no interface
253
- that they don't control and then retroactively give it an interface by way
261
+ same module. * In other words, someone can't extend a class with an interface
262
+ that they don't control and then retroactively expose its interface by way
254
263
of a subclass. This ensures that if you declare a class C with no interface,
255
264
then any object of type C will reliably be an instance of your actual class
256
265
C or some other type you control.*
@@ -271,24 +280,27 @@ particular:
271
280
* If the class has at least one generative constructor (which may be default),
272
281
it is treated as implicitly marked ` open ` .
273
282
274
- * If the class has at least one generative constructor (which may be default)
275
- and is not marked ` abstract ` it is treated as implicitly marked ` class ` .
276
-
277
283
* If the class has no non-default generative constructors, and ` Object ` as
278
- superclass, it is treated as implicitly marked ` mixin ` .
284
+ superclass, it continues to expose an implicit mixin.
279
285
280
286
[ language versioning ] : https://dart.dev/guides/language/evolution#language-versioning
281
287
282
288
When updating a library to the language version that supports modules, you'll
283
289
want to decide what capabilities to offer, or just place all the modifiers you
284
- can to preserve the class's current behavior.
290
+ need to preserve the class's current behavior.
291
+
292
+ Migrating a class that is used both as a class and a mixin is harder. For that,
293
+ you will have to migrate it to two separate declarations and give one of them
294
+ a different name. Fortunately, classes used this way are very rare.
285
295
286
296
** TODO: Investigate tooling to automatically migrate.**
287
297
288
298
## Capability controls on members
289
299
290
300
** TODO: Do we want 'final' non-overridable members?**
291
301
302
+ ** TODO: Protected?**
303
+
292
304
## Implicit modules and legacy code
293
305
294
306
** TODO**
0 commit comments