www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - New concept Mixin Methods

reply Andre Pany <andre s-e-a-p.de> writes:
Hi,

Since a few days I think about a new concept called Mixin Methods.
I wonder whether it makes sense to write a DIP or I maybe miss an 
obvious issue with the concept?

Mixin Methods serves the purpose to enable introspection in class 
based frameworks by inheritance.

If you look at frameworks like d-unit 
(https://github.com/linkrope/dunit/blob/master/example.d) or 
arsd.jni (https://arsd-official.dpldocs.info/arsd.jni.html) they 
try to solve the issues in different ways.
In d-unit the unit test classes do not have inheritance but each 
unit test class needs to add a mixin statement `mixin UnitTest;`.

In arsd.jni there is another concept, the classes inherits 
introspection by inheriting from the root class and the current 
class as template parameter:

```d
final class Hello : JavaClass!("", Hello) {
}
```
The issue with this concept is, all classes inherits from the 
root class (JavaClass) directly. Inheritance from another child 
class (e.g. Hello) is not possible.

The concept Mixin Methods can be used like this

```d
import std;
void main()
{
   new Animal().fancyMethod();
   new Dog().fancyMethod();
   new Bulldog().fancyMethod();
}

class Animal {

     mixin void fancyMethod(){
         writeln(__traits(identifier, typeof(this)));
     }

}

class Dog : Animal {}

class Bulldog : Dog {}
```

Due to the mixin statement, the compiler will copy the 
fancyMethod into all sub classes of class Animal as method 
overload.

```d
class Dog : Animal {
// compiler
override void fancyMethod(){
writeln(__traits(identifier, typeof(this)));
}
// compiler
}

class Bulldog : Dog {
// compiler
override void fancyMethod(){
writeln(__traits(identifier, typeof(this)));
}
// compiler
}
```

As you can see, Mixin Methods works for non static and non final 
methods. The benefit is, the users just need to inherit from a 
framework class to inherit the introspection capabilities.

Does it make sense to create here a DIP or do you see any road 
blocker?

Kind regards
Andre
Dec 25 2022
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12/25/22 12:07, Andre Pany wrote:
 
 Does it make sense to create here a DIP or do you see any road blocker?
I have done this kind of thing manually in the past with mixin templates. The DMD frontend at some point turned unable to handle the combination of code generation, inheritance, recursion, and introspection. So, go for it, but I am not so sure that there will be a working implementation. Also, it would not be powerful enough for my use cases, it's sometimes useful to mixin some fields (or entire mixin templates) into child classes based on `typeof(this)`.
Dec 25 2022
parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 26/12/2022 1:03 AM, Timon Gehr wrote:
 The DMD frontend at some point turned unable to handle the combination 
 of code generation, inheritance, recursion, and introspection.
Yeah I've hit this in the past, its soured me pretty greatly on mixin templates unfortunately. I only use them sparingly now.
Dec 25 2022
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 25 December 2022 at 11:07:30 UTC, Andre Pany wrote:
 The concept Mixin Methods can be used like this

 ```d
 class Animal {

     mixin void fancyMethod(){
         writeln(__traits(identifier, typeof(this)));
     }

 }
 ```

 Due to the mixin statement, the compiler will copy the 
 fancyMethod into all sub classes of class Animal as method 
 overload.
[...]
 Does it make sense to create here a DIP or do you see any road 
 blocker?
I don't think this feature provides enough utility to justify a DIP. Ideally, we should only add new features to the core language when we cannot solve our problem any other way, and each feature we add should be as powerful and general as possible, so that we can solve a large number of problems with a small number of features. This proposal fails on both counts: the problem it solves can already be solved by using a mixin template in the subclasses, so all it really does is save a bit of typing; and it only saves that little bit of typing in this one, very specific scenario.
Dec 25 2022
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12/25/22 16:26, Paul Backus wrote:
 
 This proposal fails on both counts: the problem it solves can already be 
 solved by using a mixin template in the subclasses,
Don't try this at home. (It does not actually work.)
 so all it really 
 does is save a bit of typing; and it only saves that little bit of 
 typing in this one, very specific scenario.
I don't think it's about saving the typing, it's about not allowing bugs to appear because people _forgot_ to type.
Dec 25 2022
parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Sunday, 25 December 2022 at 15:29:40 UTC, Timon Gehr wrote:
 I don't think it's about saving the typing, it's about not 
 allowing bugs to appear because people _forgot_ to type.
This is exactly true... and it brings to mind an alternate idea: require a non-abstract method must be overridden in a child class. Consider this: class Dupable { __mustOverride("hint message") Dupable dup() { return new Dupable(); } } class Child : Dupable { // if you forgot to override dup, you'd get the wrong type out // but since the parent had mustoverride, the compiler will tell us } error: class Child must override method `Dupable dup` hint: hint message Then it prods them in the right direction and the hint message can tell them to mixin something. Destroy.
Dec 26 2022
parent Andre Pany <andre s-e-a-p.de> writes:
On Tuesday, 27 December 2022 at 01:15:42 UTC, Adam D Ruppe wrote:
 On Sunday, 25 December 2022 at 15:29:40 UTC, Timon Gehr wrote:
 I don't think it's about saving the typing, it's about not 
 allowing bugs to appear because people _forgot_ to type.
This is exactly true... and it brings to mind an alternate idea: require a non-abstract method must be overridden in a child class. Consider this: class Dupable { __mustOverride("hint message") Dupable dup() { return new Dupable(); } } class Child : Dupable { // if you forgot to override dup, you'd get the wrong type out // but since the parent had mustoverride, the compiler will tell us } error: class Child must override method `Dupable dup` hint: hint message Then it prods them in the right direction and the hint message can tell them to mixin something. Destroy.
While this works, there is a small blurring. We want to express that a specific mixin template is mixed into the sub class, but what we actually say is, that a specific method needs to be overridden. Therefore the developer can just override the method without actual mixin the mixin template. From my point of view, it would be neat if I could express that a specific mixin template should be evaluated within each child class. In the sample below the syntax `class mixin Foo;` is used. ```d import std; mixin template Foo() {     void fancyMethod(){         writeln(__traits(identifier, typeof(this)));     } } class Animal {     class mixin Foo; } class Dog : Animal {} class Bulldog : Dog {} void main() {    new Animal().fancyMethod();    new Dog().fancyMethod();    new Bulldog().fancyMethod(); } ``` Kind regards Andre
Dec 28 2022
prev sibling next sibling parent Hipreme <msnmancini hotmail.com> writes:
On Sunday, 25 December 2022 at 15:26:30 UTC, Paul Backus wrote:
 On Sunday, 25 December 2022 at 11:07:30 UTC, Andre Pany wrote:
 The concept Mixin Methods can be used like this

 ```d
 class Animal {

     mixin void fancyMethod(){
         writeln(__traits(identifier, typeof(this)));
     }

 }
 ```

 Due to the mixin statement, the compiler will copy the 
 fancyMethod into all sub classes of class Animal as method 
 overload.
[...]
 Does it make sense to create here a DIP or do you see any road 
 blocker?
I don't think this feature provides enough utility to justify a DIP. Ideally, we should only add new features to the core language when we cannot solve our problem any other way, and each feature we add should be as powerful and general as possible, so that we can solve a large number of problems with a small number of features. This proposal fails on both counts: the problem it solves can already be solved by using a mixin template in the subclasses, so all it really does is save a bit of typing; and it only saves that little bit of typing in this one, very specific scenario.
So, this is a feature that seems like the ` :autoBuild` macro in Haxe does. Although it is only additive, I can find real nice use cases for that which is impossible in current D state. So, this mixin methods would work for interfaces too? Another thing, I believe instead of of doing mixin methods, putting a way to do something like `class Test(mixin a!(), b!(), c!())` [All of those are mixin templates]. I believe that way is more readable, flexible and its syntax should be a lot easier to implement. For instance, what is impossible to do is to create a registry in your classes when they extend a base class. For game engines, it is useful doing that as you would be able to track classes creation, including creating specific factories, you could implement UI based on that without requiring the user to actually put the `mixin template`. There is a case which I coded my game assets to be able to be multithreaded loaded by putting a mixin template, it would be a lot better if I could only just generate it automatically by both extending a class or by implementing an interface (aka simulating multi inheritance).
Dec 25 2022
prev sibling parent reply Andre Pany <andre s-e-a-p.de> writes:
On Sunday, 25 December 2022 at 15:26:30 UTC, Paul Backus wrote:
 On Sunday, 25 December 2022 at 11:07:30 UTC, Andre Pany wrote:
 The concept Mixin Methods can be used like this

 ```d
 class Animal {

     mixin void fancyMethod(){
         writeln(__traits(identifier, typeof(this)));
     }

 }
 ```

 Due to the mixin statement, the compiler will copy the 
 fancyMethod into all sub classes of class Animal as method 
 overload.
[...]
 Does it make sense to create here a DIP or do you see any road 
 blocker?
I don't think this feature provides enough utility to justify a DIP. Ideally, we should only add new features to the core language when we cannot solve our problem any other way, and each feature we add should be as powerful and general as possible, so that we can solve a large number of problems with a small number of features. This proposal fails on both counts: the problem it solves can already be solved by using a mixin template in the subclasses, so all it really does is save a bit of typing; and it only saves that little bit of typing in this one, very specific scenario.
Thanks all for the valuable feedback. Yes, the essence of the idea is to make the usage of frameworks as simple as in other languages (e.g. Delphi, Java). Just by inheriting from a class, everything "just works". In languages with runtime introspection capabilities this is easy. In D here is a small gap, in addition to inheritance you need to mixin the template in every class. Yes, it is syntax sugar, but also avoids the issue that users forgets the mixin template statement. Kind regards Andre
Dec 25 2022
parent Paul Backus <snarwin gmail.com> writes:
On Sunday, 25 December 2022 at 18:02:54 UTC, Andre Pany wrote:
 Thanks all for the valuable feedback.

 Yes, the essence of the idea is to make the usage of frameworks 
 as simple as in other languages (e.g. Delphi, Java). Just by 
 inheriting from a class, everything "just works".
 In languages with runtime introspection capabilities this is 
 easy. In D here is a small gap, in addition to inheritance you 
 need to mixin the template in every class.

 Yes, it is syntax sugar, but also avoids the issue that users 
 forgets the mixin template statement.
You may find some inspiration in Herb Sutter's proposal for metaclasses in C++: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0707r4.pdf
Dec 25 2022
prev sibling next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
A long time ago I considered something similar, its a very neat idea.

Where its needed most is at the module level, for any imports get it 
mixed in. This covers pretty much anything rather than being specific to 
classes.

But there is always improving your build manager... Its been on my todo 
list for a while now (quite low), to refactor dub's generated file which 
lists all modules so that it is available for more than just unittest 
builds.
Dec 25 2022
parent Paul Backus <snarwin gmail.com> writes:
On Sunday, 25 December 2022 at 18:26:53 UTC, Richard (Rikki) 
Andrew Cattermole wrote:
 A long time ago I considered something similar, its a very neat 
 idea.

 Where its needed most is at the module level, for any imports 
 get it mixed in. This covers pretty much anything rather than 
 being specific to classes.
You're supposed to tell horror stories on Halloween, not Christmas! If you really want #include in D, you can use mixin(import("filename")). Personally, I would much prefer my modules to remain modular, please and thank you.
Dec 25 2022
prev sibling parent WebFreak001 <d.forum webfreak.org> writes:
On Sunday, 25 December 2022 at 11:07:30 UTC, Andre Pany wrote:
 Hi,

 Since a few days I think about a new concept called Mixin 
 Methods.
 I wonder whether it makes sense to write a DIP or I maybe miss 
 an obvious issue with the concept?

 Mixin Methods serves the purpose to enable introspection in 
 class based frameworks by inheritance.

 If you look at frameworks like d-unit 
 (https://github.com/linkrope/dunit/blob/master/example.d) or 
 arsd.jni (https://arsd-official.dpldocs.info/arsd.jni.html) 
 they try to solve the issues in different ways.
 In d-unit the unit test classes do not have inheritance but 
 each unit test class needs to add a mixin statement `mixin 
 UnitTest;`.

 In arsd.jni there is another concept, the classes inherits 
 introspection by inheriting from the root class and the current 
 class as template parameter:

 ```d
 final class Hello : JavaClass!("", Hello) {
 }
 ```
 The issue with this concept is, all classes inherits from the 
 root class (JavaClass) directly. Inheritance from another child 
 class (e.g. Hello) is not possible.

 The concept Mixin Methods can be used like this

 ```d
 import std;
 void main()
 {
    new Animal().fancyMethod();
    new Dog().fancyMethod();
    new Bulldog().fancyMethod();
 }

 class Animal {

     mixin void fancyMethod(){
         writeln(__traits(identifier, typeof(this)));
     }

 }

 class Dog : Animal {}

 class Bulldog : Dog {}
 ```

 Due to the mixin statement, the compiler will copy the 
 fancyMethod into all sub classes of class Animal as method 
 overload.

 ```d
 class Dog : Animal {
 // compiler
 override void fancyMethod(){
 writeln(__traits(identifier, typeof(this)));
 }
 // compiler
 }

 class Bulldog : Dog {
 // compiler
 override void fancyMethod(){
 writeln(__traits(identifier, typeof(this)));
 }
 // compiler
 }
 ```

 As you can see, Mixin Methods works for non static and non 
 final methods. The benefit is, the users just need to inherit 
 from a framework class to inherit the introspection 
 capabilities.

 Does it make sense to create here a DIP or do you see any road 
 blocker?

 Kind regards
 Andre
We have the template this parameter that would on the surface work like this feature: ```d import std; void main() {    new Animal().fancyMethod();    new Dog().fancyMethod();    new Bulldog().fancyMethod(); } class Animal { void fancyMethod(this This)(){ writeln(__traits(identifier, This)); } } class Dog : Animal {} class Bulldog : Dog {} ``` outputs ``` Animal Dog Bulldog ``` overriding works by redeclaring the method inside the sub-classes. However the problem with this is that the methods are just templates, e.g. they work like UFCS functions, only knowing the type they are being called on and not the actual type. If you cast your Bulldog to Animal, you will call fancyMethod with `Animal` as template parameter here. (breaking a bunch of OOP patterns and the advantages of using inheritance) I think some mixin-into-subclasses approach would be quite nice for a lot of things, but I think it's better if the user needs to explicitly `mixin` extra methods, so that they don't magically appear. (+ explicit mixin can specify extra options) Having some way to require users to mixin something would be something worth looking into though I think. With methods you can, at least for types directly inheriting from the abstract class, rely on linker errors. This doesn't scale well though, as it will introduce runtime bugs once inheriting from other classes that have implementations.
Dec 28 2022