dart-lang / dart-lang/language
Mixin Composition Syntax
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
Proposed solution for #540.
Dart has mixins, and the ability to apply multiple mixins at a time, but no way to combine multiple mixins into a single mixin.
Currently a *mixin* is defined as the difference between a class and its superclass. It is a set of method declarations and a set of interfaces implemented.
Mixin application applies this difference on top of a superclass to create a new class.
It is essentially what:
```dart
class A extends B implements C, D {
members
}
```
defines in the `implements C, D { members }` part, as a difference from the `B` superclass, but with a `mixin` declaration you can define this difference independently of the superclass, and then apply the same mixin to a number of superclasses:
```dart
mixin BCD on B implements C, D { members }
class A = B with BCD;
```
You sometimes want to apply more than one mixin:
```dart
mixin IterableMixin implements Iterable { ... }
mixin ListMixin on Iterable implements List { ... }
class IterableBase = Object with IterableMixin;
class ListBase = IterableBase with ListMixin;
class MyList extends ListBase { ... }
class MyList2 extends Object with IterableMixin, ListMixin { ... }
```
Here we can combine the iterable and list primitives into on *base class*, but we cannot do the same for mixins. You have to serially apply the mixins to get the same effect.
It would be *nice* and *convenient for users* if we could define a `ListMixin` mixin which *includes* the members of `IterableMixin`, without having to duplicate code.
(The current design actually duplicates code, so `ListMixin` can be applied to something which is not an `Iterable` because it has copied all the members of `IterableMixin`).
## Proposal
Allow `mixin` declarations to extend other mixins.
```dart
mixin ListMixin on Iterable extends IterableMixin {
...
}
```
If a mixin with an `extends` clause does not have an `on` clause, it inherits the one of the first mixin it extends. If it has an `on` clause, the `on` clause must be a subtype of the `on` clause of the first mixin it extends.
I say "first" because you can extend more than one mixin:
```dart
mixin Foo on Bar extends Baz, Qux implements Zip, Zap {
...
}
```
Here `Bar` must be a subtype of the `on` type of `Baz`, and the type of `Baz` must be a subtype of the `on` type of `Qux`.
The result of this mixin declaration is a *mixin composition* of then three mixins: `Baz` × Qux × `implements Zip, Zip { ... }` with an `on` type of `Bar`.
Applying a mixin composition is equivalent to applying the three mixins in order.
Inside the mixin composition declaration, members act as if their "superclass" is `Bar with Baz, Qux`. Implementations can do statically resolved super-invocations for concrete members of `Baz` or `Qux`,
The grammar is modifed as:
```dart
::= `mixin` ? (`on` )? (`extends` (`,` )*)? ((`implements` (`,` )*)? `{` * `}`)?
```
A "mixin" is now a difference between a class and a class further up the superclass chain, not necessarily the immediate superclass. A *simple mixin* has one level of class hierarchy, and a *composite* mixin has more.
The body-and-implements parts of a mixin declaration can be omitted. It is a compile-time error to have no body *and* no extends clause. This means that each mixin declaration introduces at least one level of class difference.
```dart
mixin Foo on Bar extends Baz, Qux;
```
This declares the composite of `Baz` and `Qux`, and nothing more, a two-level composite mixin, whereas
```dart
mixin Foo on Bar extends Baz, Qux {}
```
declares a *three*-level composite mixin, with the final level not adding anything.
Semantically, everything is defined in terms of applying a simple mixin to a class. Applying a composite mixin is equivalent to applying the individual levels of the composite in left-to-right (top-to-bottom) order.
Contributor guide
Research direction
Start with this proposal and related issue #540; no implementation files, tests, or entry points are named in the payload. Compare the proposed grammar and mixin-composition semantics with the current Dart language specification, then determine what accepted specification changes and compatibility decisions would constitute done.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100