Skip to content

Trailing mixins #4359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
munificent opened this issue May 6, 2025 · 1 comment
Open

Trailing mixins #4359

munificent opened this issue May 6, 2025 · 1 comment
Labels
feature Proposed language feature that solves one or more problems

Comments

@munificent
Copy link
Member

As part of simplifying augmentations (#4256), we're planning to remove support for augmented and the ability for an augmentation to wrap existing code and call it. So this wouldn't be allowed:

class C {
  foo() {
    print('introductory');
  }
}

augment class C {
  augment foo() {
    augmented();
    print('augmented');
  }
}

main() {
  C().foo(); // Prints "introductory" then "augmented".
}

Even without augmented, an augmentation could almost accomplish the same thing by applying a mixin:

class C {
  foo() {
    print('introductory');
    super.foo();
  }
}

augment class C with _M {}

mixin _M {
  foo() {
    print('augmented');
  }
}

main() {
  C().foo(); // Prints "introductory" then "augmented".
}

The difference is that method mixed in by the augmentation ends up before the introductory method instead of after it, which is why the introductory method has to call super.foo() for it to be reached.

You can think of a class body as sort of like an in-place mixin definition and application:

class C with M1, M2, M3 {
  foo() {
    print('foo');
  }
}

// Is pretty much the same as:
class C with M1, M2, M3, _ClassBody {}
mixin _ClassBody {
  foo() {
    print('foo');
  }  
}

That suggests a natural extension where we loosen the restriction that the "class body mixin" has to be last in the mixin chain. Syntactically, it could be maybe:

class C with M1, M2, {
  // Methods here...
} with M3, M4;

The mixins (including the { ... } class body one) are then applied in that order with the mixins in the trailing with clause applied after the class body itself. If we had that, then the above augmentation example could look like:

class C {
  foo() {
    print('introductory');
  }
}

augment class C {} with _M
//                 ^^^^^^^ After class body now.

mixin _M {
  foo() {
    super.foo();
    print('augmented');
  }
}

main() {
  C().foo(); // Prints "introductory" then "augmented".
}

Now the augmentation's mixed in methods correctly come after the methods in the introductory declaration. The augmented method now gets to call super.foo() to reach the introductory declaration instead of the order being reversed.

Does this feature carry its weight? Is it a good idea? Not sure. But I figured it was worth writing up.

@munificent munificent added the feature Proposed language feature that solves one or more problems label May 6, 2025
@lrhn
Copy link
Member

lrhn commented May 7, 2025

I like it.
I should, it allows overriding as the "augmented" feature, like #4308, but using mixins like #4203 (comment)

In the latter comment it is suggested that you can have multiple body blocks too, which just avoids declaring a one-use mixin with a hard-to-express on type (which would probably require an extra interface as well, to inject before the mixin application.).

The example above has this problem. The _M mixin does a super.foo invocation, but doesn't have an on clause.

To make it work, you'd need something like:

class Foo {
  void foo() {
    // ...
  }
}
abstract interface class _Foo {
  void foo();
}
mixin _M on _Foo {
  void foo() {
    // ...
    super.foo();
    // ...
  }
}
augment class Foo implements _Foo {
} with _M;

Easier if you could just write:

augment class Foo {
} with {
  void foo() {
    // ...
    super.foo();
    // ...
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

2 participants