dart-lang / dart-lang/language

Mixin Composition Syntax

Open
#541 14 comments 13 reactions 0 assignees View on GitHub
feature
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.