Simple mixin into its on class fails

154 views
Skip to first unread message

Guyren Howe

unread,
Jan 17, 2021, 6:39:12 PM1/17/21
to Dart Misc
This works:

mixin Foo on Bar {}

class Bang with Foo{}

class Bar extends Bang {
Bar() {
print('Bar');
}
}


But this:

mixin Foo on Bar {}

class Bar with Foo {
Bar() {
print('Bar');
}
}

Produces:

scratch_1.dart:2:7: Error: 'Foo' is a supertype of itself.
mixin Foo on Bar {}
      ^
scratch_1.dart:4:7: Error: 'Object with Foo' is a supertype of itself.
class Bar with Foo {
      ^
scratch_1.dart:4:7: Error: 'Bar' is a supertype of itself.
class Bar with Foo {
      ^


Oddly, Google (which makes Dart!) returns no results for the phrase “is a supertype of itself”.

Erik Ernst

unread,
Jan 18, 2021, 10:11:49 AM1/18/21
to Dart Misc
On Mon, Jan 18, 2021 at 12:39 AM Guyren Howe <guy...@gmail.com> wrote:
This works:

mixin Foo on Bar {}

class Bang with Foo{}
This should be an error, because `Object` does not implement `Bar`. It is also an error that the subtype graph is cyclic (Bang <: Foo <: Bar <: Bang). So it's not obvious what you mean by 'This works' ... ?
class Bar extends Bang {
Bar() {
print('Bar');
}
}


But this:

mixin Foo on Bar {}

class Bar with Foo {
Bar() {
print('Bar');
}
}

Produces:

scratch_1.dart:2:7: Error: 'Foo' is a supertype of itself.
mixin Foo on Bar {}
      ^
scratch_1.dart:4:7: Error: 'Object with Foo' is a supertype of itself.
class Bar with Foo {
      ^
scratch_1.dart:4:7: Error: 'Bar' is a supertype of itself.
class Bar with Foo {
      ^

These errors look fine, `Foo` is indeed a subtype of `Bar` (and that's justified because it is required that the superclass of `Foo` must implement `Bar`), and `Bar` is a subtype of `Foo` (because `Foo` occurs in its `implements` clause), and this makes all those "is a supertype of itself" claims true.

Note that the `on` clause of a mixin is intended to be used to specify which `super` invocations we can have in the body of the mixin: If we want to do `super.baz()` in the body of `Foo` then the superclass had better have a concrete implementation of `baz` that we can call; but we don't yet know which class will be the superclass of `Foo` (that's different for each mixin application `SomeSuperClass with Foo`), so we need to specify a constraint (here: the superclass of `Foo` must be a subtype of `Bar`), and then we know (almost) what we need to know as soon as we have checked that constraint.

The missing part is that the method that we wish to invoke via `super` may be abstract. This could have been a compile-time error (checked at the mixin application), but it is only detected dynamically (mainly for performance reasons). If it turns out to be a problem then it could always be detected by a lint. Here's an example:

mixin Foo on Baz {
  void foo() => super.baz();
}

class Baz {
  void baz() { print('Baz.baz'); }
}

abstract class Baz2 implements Baz {}

class Bar extends Baz2 with Foo {
  void baz() { print('Bar.baz'); }
  Bar() {
    print('Bar');
  }
}

main() {
  Bar().foo(); // Throws, because `Bar.baz` isn't a super-method as seen from `Foo`.
}

The main point here is that you should use `on` to specify which super-invocations you need in your mixin. It looks like you're using it for some other purpose.

Oddly, Google (which makes Dart!) returns no results for the phrase “is a supertype of itself”.

--
For more ways to connect visit https://dart.dev/community
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.
To view this discussion on the web visit https://groups.google.com/a/dartlang.org/d/msgid/misc/61C9B215-E514-4B76-9DC2-472387B1C63F%40gmail.com.


--
Erik Ernst  -  Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 København K, Denmark
CVR no. 28866984

Guyren Howe

unread,
Jan 18, 2021, 6:01:39 PM1/18/21
to Dart Misc
Thanks. Hmm.

Apparently I mixed something up with my example. Sorry.

If I have a class, and I want to extra a method from it into a mixin (where the mixin has to be ‘on’ the original class), and then mix that mixin back into the class, how do I proceed?

The reason to do this is that I want other classes to be able to implement my class, but still share some code with the class.

It appears from the discussion that I can’t do this without just copy-pasting the code from the mixin into my class, or requiring every subclass to mix in the code, neither of which appeals.

Erik Ernst

unread,
Jan 19, 2021, 7:18:42 AM1/19/21
to Dart Misc
If you're saying that the mixin `M` has to be `on` the original class `C` then you have also said that the mixin is intended to be applied to `C` or a subtype thereof for the purpose of calling `super.m` (or `super.m(...)`, or `super + something` for the member `operator +`, etc.) for some members `m` that belong to the interface of `C` (so `C` declares or inherits those members).

If the body of `M` does not contain such super-invocations then `M` does not have to be `on C`.

In any case, if you make `M on C` then `M` can be used as `class D extends C1 with M {}` where `C1` is `C` or some subtype of `C`. You can't have `class C with M {}`, and it doesn't make sense to do that, anyway, because `C` must be the superclass of `M` or a supertype of the superclass of `M`, and then it can't be a subtype as well.

and then mix that mixin back into the class, how do I proceed?

The reason to do this is that I want other classes to be able to implement my class, but still share some code with the class.

It depends on what you are doing in the method that you are extracting.

Let's say the extracted method is `void foo()`, and it calls `void bar()` on the receiver. In that case you just need to ensure that `this` has a `bar` with the right signature, and you could do that by declaring it in the mixin:

  mixin M {
    void bar();
    void foo() => bar();
  }

If you need to have a super-invocation (say, `super.bar()`) then you'll have to specify that `M` is `on B` where `B` is a class that has a suitable declaration of `bar`.
 
It appears from the discussion that I can’t do this without just copy-pasting the code from the mixin into my class, or requiring every subclass to mix in the code, neither of which appeals.

That's not true, but you do need to ensure that the mixin refers to known entities: If you wish to use a member `m` on `this` then it must declare `m` (possibly abstractly) or the `implements` clause must mention a class that has an `m`; if you wish to perform a super-invocation then it must be supported by the `on` class.

--
For more ways to connect visit https://dart.dev/community
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Guyren Howe

unread,
Jan 23, 2021, 4:51:24 PM1/23/21
to Dart Misc, Erik Ernst
I don't follow this. Let me start over.

I have a class like this:

class Foo {
  void x() {
    y();
  }

  void y() {
    print('y');
  }
}

I will have some classes that extend Foo and others that implement Foo. The ones that implement Foo will all need x(), which depends on a method in Foo.

So what I want to do is to move x() to some XMixin on Foo so the implementers can just do with XMixin, but have it already provided for classes that extend Foo. How can I do this without such evils as copy-pasting the code?

Guyren Howe

unread,
Jan 23, 2021, 5:14:53 PM1/23/21
to Dart Misc, Guyren Howe, Erik Ernst
This useless, empty subclass makes it work.

mixin XMixin on AbstractFoo {
  void x() {
    y();
  }
}

abstract class AbstractFoo {

  void y() {
    print('y');
  }
}

class Foo extends AbstractFoo with XMixin {}

class Bar extends Foo {
  void doIt() => x();
}

void main() {
  Bar().doIt;
}

Not a fan of this.

Erik Ernst

unread,
Jan 24, 2021, 3:00:56 PM1/24/21
to Guyren Howe, Dart Misc
On Sat, Jan 23, 2021 at 10:51 PM Guyren Howe <guy...@gmail.com> wrote:
I don't follow this. Let me start over.

I have a class like this:

class Foo {
  void x() {
    y();
  }

  void y() {
    print('y');
  }
}

I will have some classes that extend Foo and others that implement Foo. The ones that implement Foo will all need x(), which depends on a method in Foo.

So what I want to do is to move x() to some XMixin on Foo

I mentioned that you only need `on Foo` if you must perform super-invocations (like `super.x()`) of methods declared by `Foo`. There are no super-invocations in your example so you cannot possibly need `on` anything.

On the other hand, you must ensure that methods on `this` (invoked explicitly as `this.m()` or implicitly as `m()`) that you must call in the body of the mixin are statically known to exist. As I mentioned, one way to do this is to have `implements OtherClass` where `OtherClass` has the desired members, and another way is to simply declare the members that you need as abstract in the mixin. Let's take the latter first:

mixin FooMixin {
  void x() => y();
  void y();
}

class Foo with FooMixin {
  void y() => print('y');
}

class FooSubclass extends Foo {}

class FooSubtype with FooMixin implements Foo {
  void y() {
    /* Do whatever is appropriate */
  }
}

If you consider the abstract declaration `void y();` as too much copy-paste then you can use implements:

class FooBase {
  void y() => print('y');  
}

mixin FooMixin implements FooBase {
  void x() => y();
}

class Foo extends FooBase with FooMixin {}

class FooSubclass extends Foo {}

class FooSubtype with FooMixin implements Foo {
  void y() {
    /* Do whatever is appropriate */
  }
}

This is actually exactly the same class hierarchy that you mention, except that `FooBase` is concrete whereas your `AbstractFoo` is abstract (there is no need to make it abstract).

The point is that the "empty" class `class Foo extends FooBase with FooMixin {}` isn't useless: `FooBase` only needs to contain abstract declarations (so if it's helpful then you can make `FooBase.y` abstract and implement it in `Foo`), and `FooBase` only needs to contain declarations of members that the mixin methods are calling (so you could have lots of other methods in `Foo`, as long as they aren't called by any methods in `FooMixin`). Finally, `Foo` combines `FooBase` and `FooMixin`, which means that it isn't "empty", it specifies a non-empty amount of construction work.

You could even claim that this kind of construction is more powerful than the regular approach where construction is done by writing a class body containing some member declarations. You could call it higher-order construction because it abstracts away from the class bodies and only mentions the names of entities whose bodies must be combined. If you develop a habit of using higher-order construction you may even like it... ;-)

Guyren Howe

unread,
Jan 24, 2021, 5:28:24 PM1/24/21
to Erik Ernst, Dart Misc
mixin XMixin on AbstractFoo {
  void x() {
    y();
  }
}

abstract class AbstractFoo {
  void y() {
    print('y');
  }
}

On Jan 24, 2021, at 12:00 , Erik Ernst <eer...@google.com> wrote:

I mentioned that you only need `on Foo` if you must perform super-invocations (like `super.x()`) of methods declared by `Foo`. There are no super-invocations in your example so you cannot possibly need `on` anything.

What?

How can I call y() in XMixin.x() without declaring that it is on a class that has y()?

Guyren Howe

unread,
Jan 24, 2021, 6:16:38 PM1/24/21
to Erik Ernst, Dart Misc
On Jan 24, 2021, at 12:00 , Erik Ernst <eer...@google.com> wrote:

On the other hand, you must ensure that methods on `this` (invoked explicitly as `this.m()` or implicitly as `m()`) that you must call in the body of the mixin are statically known to exist. As I mentioned, one way to do this is to have `implements OtherClass` where `OtherClass` has the desired members, and another way is to simply declare the members that you need as abstract in the mixin. Let's take the latter first:

mixin FooMixin {
  void x() => y();
  void y();
}

class Foo with FooMixin {
  void y() => print('y');
}

class FooSubclass extends Foo {}

class FooSubtype with FooMixin implements Foo {
  void y() {
    /* Do whatever is appropriate */
  }
}

If you consider the abstract declaration `void y();` as too much copy-paste then you can use implements:

class FooBase {
  void y() => print('y');  
}

mixin FooMixin implements FooBase {
  void x() => y();
}

class Foo extends FooBase with FooMixin {}

class FooSubclass extends Foo {}

class FooSubtype with FooMixin implements Foo {
  void y() {
    /* Do whatever is appropriate */
  }
}

This is actually exactly the same class hierarchy that you mention, except that `FooBase` is concrete whereas your `AbstractFoo` is abstract (there is no need to make it abstract).

My mental model was that ‘on’ was doing what you’re here doing with ‘implements’. Would you explain the difference?

This use of ‘implements’ is interesting. I expected that the compiler would complain about this use of ‘implements’, because the mixin clearly doesn’t implement anything from FooBase. Is it that ‘implements’ is treated differently on Mixins? If not, my mental model of what ‘implements’ is doing is all wrong. Are these subtleties documented anywhere?


The point is that the "empty" class `class Foo extends FooBase with FooMixin {}` isn't useless: `FooBase` only needs to contain abstract declarations (so if it's helpful then you can make `FooBase.y` abstract and implement it in `Foo`), and `FooBase` only needs to contain declarations of members that the mixin methods are calling (so you could have lots of other methods in `Foo`, as long as they aren't called by any methods in `FooMixin`). Finally, `Foo` combines `FooBase` and `FooMixin`, which means that it isn't "empty", it specifies a non-empty amount of construction work.

I’m trying to make my code as simple as possible for users and readers of the classes. This involves a good deal of noise and non-obvious dancing about just to please the compiler. I want to extract a method then mix it back in, and I can only do it by introducing all this otherwise pointless infrastructure.

Erik Ernst

unread,
Jan 25, 2021, 5:17:29 AM1/25/21
to Guyren Howe, Dart Misc
On Mon, Jan 25, 2021 at 12:16 AM Guyren Howe <guy...@gmail.com> wrote:


On Jan 24, 2021, at 12:00 , Erik Ernst <eer...@google.com> wrote:

On the other hand, you must ensure that methods on `this` (invoked explicitly as `this.m()` or implicitly as `m()`) that you must call in the body of the mixin are statically known to exist. As I mentioned, one way to do this is to have `implements OtherClass` where `OtherClass` has the desired members, and another way is to simply declare the members that you need as abstract in the mixin. Let's take the latter first:

mixin FooMixin {
  void x() => y();
  void y();
}

class Foo with FooMixin {
  void y() => print('y');
}

class FooSubclass extends Foo {}

class FooSubtype with FooMixin implements Foo {
  void y() {
    /* Do whatever is appropriate */
  }
}

If you consider the abstract declaration `void y();` as too much copy-paste then you can use implements:

class FooBase {
  void y() => print('y');  
}

mixin FooMixin implements FooBase {
  void x() => y();
}

class Foo extends FooBase with FooMixin {}

class FooSubclass extends Foo {}

class FooSubtype with FooMixin implements Foo {
  void y() {
    /* Do whatever is appropriate */
  }
}

This is actually exactly the same class hierarchy that you mention, except that `FooBase` is concrete whereas your `AbstractFoo` is abstract (there is no need to make it abstract).

My mental model was that ‘on’ was doing what you’re here doing with ‘implements’.

Thought so. ;-)
 
Would you explain the difference?

The `on` type specifies the interface on `this` for super-invocations (like `super.m()`). The `implements` types specify which methods you can call on `this` (not `super`). That applies to explicit invocations such as `this.m()`, and it also applies to implicit invocations like `m()` in the case where `m()` means `this.m()`.

With `on C`, members of `C` can also be called on `this`. This means that it is possible, in some situations, to use `on C` or `implements C`, and they may seem to be equivalent.

However, your example shows exactly one of those situations where the difference does matter. In general, you should always prefer `implements C` over `on C` whenever possible (that is: unless you must use a super-invocation like `super.m()` for some member `m` of `C` in the body of the mixin), because `implements C` allows the mixin to be used in a greater set of circumstances.

This use of ‘implements’ is interesting. I expected that the compiler would complain about this use of ‘implements’, because the mixin clearly doesn’t implement anything from FooBase. Is it that ‘implements’ is treated differently on Mixins?

You need to consider a mixin as an abstract declaration. Note that there can't be any instances of (exactly) a mixin, it will always be mixed into a class (and that class may be concrete or abstract, depending on whether it implements all members in its interface), and that makes a mixin similar to an abstract class.

As in an abstract class declaration, `implements C` in a declaration of a mixin `M` specifies the set of members that any concrete subtype of `M` must have. It doesn't require the abstract class or the mixin to have a concrete implementation of all those members, it just specifies that "somebody" must implement them in any given object where this code runs. That's fine and sound:

Given that the code in `M` will always run in a context where the current instance `this` has a type which is a concrete class `D` which is a subclass of a class that mixes in `M`, it is safe to assume that there will be an implementation of every member in the interface of `M`, and that includes every member which is specified by any class `C` where `M` has `implements C`.

If not, my mental model of what ‘implements’ is doing is all wrong. Are these subtleties documented anywhere?

You just need to notice that an abstract declaration can have an `implements` clause that makes more promises than it fulfills itself, and it's up to each concrete subtype to ensure that everything has an implementation. And then you need to take into account that a mixin is an abstract declaration.
The point is that the "empty" class `class Foo extends FooBase with FooMixin {}` isn't useless: `FooBase` only needs to contain abstract declarations (so if it's helpful then you can make `FooBase.y` abstract and implement it in `Foo`), and `FooBase` only needs to contain declarations of members that the mixin methods are calling (so you could have lots of other methods in `Foo`, as long as they aren't called by any methods in `FooMixin`). Finally, `Foo` combines `FooBase` and `FooMixin`, which means that it isn't "empty", it specifies a non-empty amount of construction work.

I’m trying to make my code as simple as possible for users and readers of the classes.

Sounds good!

This involves a good deal of noise and non-obvious dancing about just to please the compiler. I want to extract a method then mix it back in, and I can only do it by introducing all this otherwise pointless infrastructure.

I hope you can see why it isn't all so pointless after all. ;-)

  best regards,
Reply all
Reply to author
Forward
0 new messages