Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Side by Side Diff: docs/language/informal/mixin-declaration.md

Issue 2954653002: Add proposal for new mixin declaration syntax. (Closed)
Patch Set: Address comments. Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Dart 2.0 Mixins
2
3 Author: [lrn@google.com](mailto:lrn@google.com)
4
5 Version 0.6 (2017-06-14)
6
7 Status: Mostly designed, ready for external comments.
8
9 ## Proposal
10 This proposal introduces a new syntax for declaring mixins, separate from derivi ng a mixin from a class declaration. It expects to deprecate and remove the abil ity to derive a mixin from a class declaration, but doesn't require it.
11
12
13 ## Background
14
15 Dart 1 mixins have the following features:
16
17 * Derived from a class declaration.
18 * Applied to a superclass to create a new class.
19 * May be derived from class with super-class, then application must be on clas s implementing super-class interface.
20 * May have super-invocations if the mixin class has a super-class.
21 * Cannot have generative constructors.
22 * Mixin application forwards non-const constructors.
23
24 There are a number of problems with this approach, especially the super-class co nstraints.
25
26 * The super-calls (`super.foo()`) are not statically guaranteed to hit a match ing method. There is no specified static check of a mixin application that ensur es that any mixed-in methods containing a super-call will actually hit an existi ng method. If the superclass is abstract, the super-call may fail dynamically.
27 * Deriving a mixin from a class means that moving a method from the class to i ts superclass is a breaking change, not just a refactoring. Many class changes t hat are generally considered safe in OO languages are breaking if the class is u sed as a mixin. For that reason, we have guidelines saying not to use a class as a mixin unless it's documented as being intended as such (the creator has opted in to the extra constraints).
28 * The super-class constraint on a "mixin" is derived from the `extends` clause which only allows a single type. There is no way to specify two requirements, a nd users trying to do so ends up with code that doesn't work like they expect.
29 * A mixin derived from a mixin-application might have a different super-class than expected.
30 * Nobody understands how the super-feature actually works (http://dartbug.com/ 29758, http://dartbug.com/25765)
31 * When any class can be used as a mixin, there are local optimizations that ca nnot be performed (like DDC not being able to detect that a private field isn't overridden). Also, if a class that is not intended as a mixin is used as a mixin , many otherwise safe refactorings (e.g., moving a method to a superclass) will be breaking.
32
33
34 ### Mixin Declaration
35
36 To avoid some of the problems mentioned above, we introduce a *mixin declaration syntax* separate from class declarations:
37
38 *mixinDeclaration* : *metadata*? 'mixin' *identifier* *typeParameters*? <br>
39 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;('requires' *types*)? ('implemen ts' *types*)? '{' <em>mixinMember</em>* '}'
40
41 The `mixinMember` production allows the same instance or static members that a c lass would allow, but no constructors (for now).
42
43 The `mixin` word will have to be at least a **built-in identifier** to avoid par sing ambiguities. It does not need to be a reserved word.
44
45 It might be possible to just use `mixin` as a contextual keyword, but it would r equire some look-ahead to determine whether an occurrence is a type named `mixin ` or a mixin declaration, and we would like to discourage the former anyway.
46
47 #### Meaning
48
49 A mixin declaration introduces a mixin and an *interface*, but *not a class*. Th e mixin derived from a mixin declaration contains all the non-static members dec lared by the mixin, just as the mixin derived from a class currently does.
50
51 The interface of `mixin A requires B, C implements D, E { body }`, which has the same name as the mixin (`A` here), is equivalent to the interface that would be derived from the class declaration:
52 ```dart
53 abstract class A implements B, C, D, E { body' }
54 ```
55 where `body'` contains abstract declarations corresponding to the instance membe rs of `body` of the mixin `A`.
56
57 The `requires` keyword on the mixin declaration is open for discussion. It's bet ter than `super`, but fairly long. Another option is `on`.
58
59 It's a static warning (strong-mode error) if an instance method in a mixin body has a super-access (`super.foo`, `super.foo()`, `super + bar`, etc.) that is
60
61 * not declared by at least one of the *types* in the mixin's `requires` declar ation, or
62 * not type-compatible with at least one such declaration from a `requires` typ e.
63
64 The mixin cannot be marked as `abstract`.
65 All mixins are effectively abstract because they don't need to implement the mem bers of the required superclass types.
66 We could say that a mixin must implement all other members than the ones declare d by the required superclass types, and then allow the declaration to be marked as `abstract` if it doesn't.
67 It would still require mixin applications to be marked independently, so there i s no large advantage to marking the mixin itself as non-abstract.
68
69
70 ### Mixin application
71
72 Mixin application syntax is unchanged. A mixin application `S with M` introduces a *class* with superclass `S`, implementing `M` and with copies of all the non- static members of `M`. As usual code in the copies of members retain the static scope of their original declaration.
73
74 In a mixin application declaration `class C = S with M;`, the class is named `C` , otherwise it has a fresh name (it's effectively anonymous since nobody knows i ts name, but it needs a name because constructor names include the class name an d forwarding constructors need to have names).
75
76 Multiple applications introduce a chain of classes, so `S with M1, M2` has an an onymous `S with M1` application class as superclass and applies `M2` to that.
77
78 Mixin application semantics is mostly unchanged, except that it's a static warni ng (strong mode error) to apply a mixin to a class that doesn't implement *all* the `requires` types of the mixin declaration.
79
80 All non constructors of the superclass causes a forwarding constructor to be add ed to the mixin application with the same arguments.
81
82
83 #### Super-calls of mixin applications must be valid
84
85 Currently, the specification doesn't warn at compile-time if a `super`-invocatio n targets an abstract method. This allows declaring a mixin that extends an abst ract interface, but it also means that mistakes are only runtime-errors. We want to fix that.
86
87 * One solution is to *require the superclass of a mixin application to be non- abstract*. This would ensure that all `super`-invocations in mixin applications are valid. The mixin declaration only allows `super`-invocations declared by the ir `requires` constraints and the mixin application requires the superclass to s atisfy those constraints, and by also being non-abstract, there must be an actua l implementation of the superclass method.
88 * Alternatively, we only make it a compile-time error if a mixin application i ntroduces a method on the mixin application class which contains a super-access (<code>super.<em>x</em></code>, <code>super.<em>x</em>(...)</code>, <code>super <em>op</em> arg</code>, etc), and the actual superclass of the mixin application doesn't have a non-abstract implementation of a member with *that* name.
89 (The compile-time error applies to the mixed-in method with the super-call, so a lazy-compile-time-error implementation can fail to compile that method only.)
90
91 Obviously, if the superclass is not abstract, this check won't be necessary.
92
93 * As a third alternative, we can add syntax to explicitly declare and expose t he super constraints. Syntax could be like an abstract method that is marked as "super", perhaps one of:
94
95 ```
96 int super.foo(int bar);
97 super int foo(int bar);
98 super {
99 int foo(int bar); // and perhaps multiple declaration in the block.
100 }
101 super foo; // comma separated list of just the names.
102 super { foo }; // ditto.
103 ```
104
105
106 The block approach is unlike anything else we do in Dart. The `super.foo` declar ation is also different from other syntax and would complicate the grammar more than just a prefixed `super`, but if it's easier to understand for the user, it' s probably worth it.
107
108 Just mentioning the super-member by name is shorter, and since the required supe r-types are specified elsewhere, it should be sufficient, but it's not as readab le as a full declaration.
109
110 With any of these syntaxes, the mixin declaration explicitly declares which supe r-calls it uses, so the user can be aware of it.
111
112 On the other hand, that means duplication (you already make the super-call, now you also repeat it as a declaration) and it still locks you into not being able to do more super-calls in the future without breaking things.
113
114 If the constraints are handled entirely structurally, and don't need to be linke d to a declared required superclass constraint, it would allow mixins to be used on arbitrary objects that satisfy the constraint, but that would also be the on ly place in Dart where we have a structural constraint on classes. I would recom mend only allowing references to members of the already required superclasses.
115
116
117 The second and third options are the more permissive ones, but that also comes w ith a cost of maintainability and usability. If a mixin adds a new super-invocat ion, then it may break existing mixin applications. It's not possible to see the actual requirements of the mixin from its type signature alone - in the third o ption, a new syntax is introduced to represent the requirement.
118
119 If the requirement is just that the superclass is non-abstract (first option), t here are no hidden or fragile constraints in the relation between the mixin and the superclass. For that reason, I recommend we pick the that approach (require that the superclass of a mixin application is not abstract) and potentially loos en it later if necessary - adding explicit super-requirements would then allow a bstract superclasses that satisfy the requirements, not writing anything still w orks with a non-abstract superclass.
120
121 We should check whether that is a problem for existing code that has an abstract superclass for a mixin application.
122
123 In either case, this requirement is new. The current specification doesn't have it, instead it just silently accepts a mixin application on an abstract supercla ss that doesn't actually implement the super-member, and the call will fail at r untime.
124
125
126 ### Potential future changes
127
128
129 #### Deprecating derived mixins
130
131 In the future, preferably already in Dart 2.0, we'll remove the ability to deriv e a mixin from a class declaration.
132
133 The requires existing code to be rewritten. The rewrite is simple:
134
135
136 ```dart
137 class FooMixin extends S implements I {
138 members;
139 }
140 ```
141
142 becomes
143
144 ```dart
145 mixin FooMixin requires S implements I {
146 members;
147 }
148 ```
149
150 If a class is *actually* used as both a class and a mixin, the mixin needs to be extracted:
151
152 ```dart
153 class Foo extends S implements I { // Used as mixin *and* class
154 members;
155 }
156 ```
157
158 becomes
159
160 ```dart
161 class Foo = S with FooMixin {
162 public static members
163 }
164 mixin FooMixin requires S implements I {
165 instance members (references to statics prefixed with "Foo.")
166 }
167 // All uses of "with Foo" changed to "with FooMixin".
168 ```
169
170 Apart from public static members (which are rare) this is basically a two line r ewrite locally, and then finding the uses of the class.
171
172 Optionally, we can also allow mixins to be used as classes (instead of the other way around), so `class C extends Mixin { … }` is equivalent to `class C extends Object with Mixin { … }`.
173
174
175 #### Forward const constructors
176
177 A mixin application forwards generative constructors as non-const, even if the s uperclass constructor is a const constructor. That makes some use-cases for mixi ns impossible.
178
179 We could make the forwarding constructors const as well when it is safe. An appr oximation of that could be when the mixin declares no fields. This is not necess arily a good idea, since it would break getter/field symmetry and prevent a mixi n from changing a getter to a final field.
180
181
182 #### Extending mixins
183
184 With separate syntax for mixins, we are open to adding more capabilities without needing it to also work for classes.
185
186 Options are:
187
188 * Composite mixins (mixin can `extend` another mixin, application applies both ).
189 * Constructors (mixin constructors don't forward to the superclass, only to a super-mixin). If a mixin has generative constructors (and even const ones), ther e will be no automatic constructor forwarding because the mixin-application clas s would need to call the mixin constructor explicitly. It can be omitted if the mixin has a no-arguments constructor, which it will then have by default.
190
191
192 ### Revisions
193
194 v0.5 (2017-06-12) Initial version
195
196 v0.6 (2017-06-14) Say `mixin` must be built-in identifier.
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698