www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Inheritance of mixin

reply Andrew Benton <Andrew.Benton675 gmail.com> writes:
I'm running into a set of problems where both inheritance and 
mixin-added functionality really lend a hand to my project.  
Unfortunately, they don't necessarily work well together, as 
certain mixins need to be restated for each inheritor.

As a toy example, if I wanted to mixin some functionality to 
write names:

mixin template WriteName()
{
     string writeName()
     {
         import std.uuid : randomUUID;
         static if(__traits(hasMember, this, "name"))
             return __traits(getMember, this, "name);
         else
             return randomUUID.toString;
     }
}
class Base
{
     mixin WriteName;
}
class Inheritor : Base
{
     version(both) mixin WriteName;
     string name = "test";
}

In the above example, unless I used version "both", I would see 
only random UUIDs from instances of Base and Inheritor, however 
the functionality that I really want is to get random UUIDs only 
from Base, and get "test" from Inheritor.  So far as I know, the 
easiest way to fix this is to add that mixin to each descendant 
of Base.

I recognize that, at least for this toy example, an easier fix is 
to use a templated function, however I'm more interested in 
libraries like jsonizer and witchcraft.

So to the point: Is there an easier way to do this that I'm 
missing?  Is there a language-design reason that mixed in 
templates can't inherit?  It seems like an intuitive use of mixin.
Apr 28 2016
next sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
On Thursday, 28 April 2016 at 10:21:34 UTC, Andrew Benton wrote:
 So to the point: Is there an easier way to do this that I'm 
 missing?  Is there a language-design reason that mixed in 
 templates can't inherit?  It seems like an intuitive use of 
 mixin.
Mixins are (mostly) just a convenient way to generate code, given some parameters. They aren't supposed to work any differently than the same code would if you instantiated the mixin by hand and inserted its contents at each usage site. (I'll skip your example code here, since it has some problems and won't compile without significant changes.) The manual (mixin-free) code to do what you want looks like this: class Base { string writeName() { import std.uuid : randomUUID; return randomUUID.toString; } } class Inheritor : Base { override string writeName() { return name; } string name = "test"; } Doing this manually requires that some code be added to Inheritor. Using a mixin doesn't - and shouldn't - change this. What a mixin could do for you in this situation, is consolidate duplicate code if there are many Inheritor classes: mixin template WithName(string name0) { override string writeName() { return name; } string name = name0; } class Inheritor1 : Base { mixin WithName!"test1"; } class Inheritor2 : Base { mixin WithName!"test2"; } Alternatively, you could just use inheritance (assuming that your needs can reasonably be expressed through single inheritance): class NamedBase : Base { override string writeName() { return name; } string name; this(string name0) { name = name0; } } class Inheritor3 : NamedBase { this() { super("test3"); } } class Inheritor4 : NamedBase { this() { super("test4"); } } Try it on DPaste: http://dpaste.dzfl.pl/5595bd09391f
Apr 28 2016
parent reply Andrew Benton <Andrew.Benton675 gmail.com> writes:
On Thursday, 28 April 2016 at 12:28:23 UTC, tsbockman wrote:
 On Thursday, 28 April 2016 at 10:21:34 UTC, Andrew Benton wrote:
 So to the point: Is there an easier way to do this that I'm 
 missing?  Is there a language-design reason that mixed in 
 templates can't inherit?  It seems like an intuitive use of 
 mixin.
Mixins are (mostly) just a convenient way to generate code, given some parameters. They aren't supposed to work any differently than the same code would if you instantiated the mixin by hand and inserted its contents at each usage site.
I understand that is the point of mixin, as it exists today. It also seems like it isn't capable of what I want.
 (I'll skip your example code here, since it has some problems 
 and won't compile without significant changes.)
I'm not sure what issues you're having that are significant. I forgot to close a string, but the rest compiles and executes for me.
 Doing this manually requires that some code be added to 
 Inheritor. Using a mixin doesn't - and shouldn't - change this.
What I'm trying to get at is that I want to add behavior to a hierarchy. I know that I can get that done in a _really_ light example with a templated function, or in a heavier example by repeating my code. The problem with the former is that it doesn't actually cover my use case. The problem with the later is that any architect that sees that amount of repetition will likely start pulling their hair out. What are the reasons that mixin shouldn't be able to be used to change the behavior of a whole hierarchy?
Apr 28 2016
parent reply tsbockman <thomas.bockman gmail.com> writes:
On Friday, 29 April 2016 at 00:32:54 UTC, Andrew Benton wrote:
 On Thursday, 28 April 2016 at 12:28:23 UTC, tsbockman wrote:
 (I'll skip your example code here, since it has some problems 
 and won't compile without significant changes.)
I'm not sure what issues you're having that are significant. I forgot to close a string, but the rest compiles and executes for me.
With version = both : Deprecation: implicitly overriding base class method app.Base.WriteName!().writeName with app.Inheritor.WriteName!().writeName deprecated; add 'override' attribute Trying again, I see that it actually does compile since it's a deprecation, and not an error - but still this should be fixed, and doing so requires restructuring your code.
 What I'm trying to get at is that I want to add behavior to a 
 hierarchy.  I know that I can get that done in a _really_ light 
 example with a templated function, or in a heavier example by 
 repeating my code.  The problem with the former is that it 
 doesn't actually cover my use case.  The problem with the later 
 is that any architect that sees that amount of repetition will 
 likely start pulling their hair out.
That's quite an exaggeration. One extra line of code per derived class is not a hair-pulling level or repetition.
 What are the reasons that mixin shouldn't be able to be used to 
 change the behavior of a whole hierarchy?
Mixins are a fairly simple and elegant feature both to use, and to implement in the compiler. Your proposal kills that simplicity, in the name of saving one line of code per class for a rare use case.
Apr 28 2016
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 29.04.2016 07:35, tsbockman wrote:
 What are the reasons that mixin shouldn't be able to be used to change
 the behavior of a whole hierarchy?
Mixins are a fairly simple and elegant feature both to use, and to implement in the compiler. Your proposal kills that simplicity,
It is not complicated, we'd just need a way to specify that a mixin should be inserted into all subclasses.
 in the name of saving one line of code per class for a rare use case.
The use case isn't rare (it is a quite popular request, at least four independent instances that I remember). Having a mixin in all classes in some subtree of the class hierarchy can be a good idea. Of course one can easily do it manually, but it is always nice to get rid of boilerplate.
Apr 29 2016
next sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
On Friday, 29 April 2016 at 10:59:20 UTC, Timon Gehr wrote:
 On 29.04.2016 07:35, tsbockman wrote:
 in the name of saving one line of code per class for a rare 
 use case.
The use case isn't rare (it is a quite popular request, at least four independent instances that I remember).
Rare as in, "effecting only a very small amount of real world code" - not as in "effecting only a very small number of people".
Apr 29 2016
next sibling parent Mathias Lang via Digitalmars-d <digitalmars-d puremagic.com> writes:
2016-04-29 21:11 GMT+02:00 tsbockman via Digitalmars-d <
digitalmars-d puremagic.com>:

 On Friday, 29 April 2016 at 10:59:20 UTC, Timon Gehr wrote:

 On 29.04.2016 07:35, tsbockman wrote:

 in the name of saving one line of code per class for a rare use case.
The use case isn't rare (it is a quite popular request, at least four independent instances that I remember).
Rare as in, "effecting only a very small amount of real world code" - not as in "effecting only a very small number of people".
Anyone wanting to implement double dispatch would benefit from it, for starters. Like DMD. As OP showed, there are others use cases which are legit. That's something one usually expects from D, given the amount of boilerplate you can avoid compared to other languages.
Apr 29 2016
prev sibling parent reply Andrew Benton <Andrew.Benton675 gmail.com> writes:
On Friday, 29 April 2016 at 19:11:24 UTC, tsbockman wrote:
 Rare as in, "effecting only a very small amount of real world 
 code" - not as in "effecting only a very small number of 
 people".
I'm not sure if actually affects just a small number of real world cases. I think that majority of medium to large projects will want to apply some sort of behavior to multiple hierarchies. Additionally, any libraries that provide a base class with a mixin require inheritors to know about that mixin and provide it in their own code. It certainly isn't going to happen in every case, but if the needed functionality is provided by mixins, then users of the library could have problems.
Apr 29 2016
next sibling parent reply Ed <Ed ed.de> writes:
On Saturday, 30 April 2016 at 01:06:18 UTC, Andrew Benton wrote:
 On Friday, 29 April 2016 at 19:11:24 UTC, tsbockman wrote:
 Rare as in, "effecting only a very small amount of real world 
 code" - not as in "effecting only a very small number of 
 people".
[...] Additionally, any libraries that provide a base class with a mixin require inheritors to know about that mixin and provide it in their own code. [...]
It looks like you miss the solution proposed above, using a template this parameter. It's very clear and consise ! Actually the mixin is even not necessary.
Apr 30 2016
parent Andrew Benton <Andrew.Benton675 gmail.com> writes:
On Saturday, 30 April 2016 at 22:14:47 UTC, Ed wrote:
 On Saturday, 30 April 2016 at 01:06:18 UTC, Andrew Benton wrote:
 On Friday, 29 April 2016 at 19:11:24 UTC, tsbockman wrote:
 Rare as in, "effecting only a very small amount of real world 
 code" - not as in "effecting only a very small number of 
 people".
[...] Additionally, any libraries that provide a base class with a mixin require inheritors to know about that mixin and provide it in their own code. [...]
It looks like you miss the solution proposed above, using a template this parameter. It's very clear and consise ! Actually the mixin is even not necessary.
Unfortunately the solution with the templated "this" creates non-virtual functions. Using the template and the classes in that solution, the following main method produces two UUIDs: void main() { Base a = new Base(), b = new Inheritor(); a.writeName.writeln; b.writeName.writeln; } which is very different from the output that we would want by having the specialization of Base.
Apr 30 2016
prev sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
On Saturday, 30 April 2016 at 01:06:18 UTC, Andrew Benton wrote:
 Additionally, any libraries that provide a base class with a 
 mixin require inheritors to know about that mixin and provide 
 it in their own code.  It certainly isn't going to happen in 
 every case, but if the needed functionality is provided by 
 mixins, then users of the library could have problems.
I spent a while trying to figure out how to automatically verify that a mixin required by a class hierarchy is included in all derived classes, and failed to come up with a general purpose solution. (I found a partial solution involving a `this T` templatized constructor, but it had a lot of problems.) So, I agree now that this is a problem worth solving. It could be fixed by extending template mixins as you and others have proposed. Another possible solution would be to fix `this T` template functions to be virtual, and automatically generate an override for each derived class. This would directly solve the most common use case, and also provide an (awkward) way to automatically verify mixin inclusion in derived classes for more complex scenarios.
May 02 2016
parent reply Basile B. <b2.temp gmx.com> writes:
On Monday, 2 May 2016 at 16:41:01 UTC, tsbockman wrote:
 On Saturday, 30 April 2016 at 01:06:18 UTC, Andrew Benton wrote:
 Additionally, any libraries that provide a base class with a 
 mixin require inheritors to know about that mixin and provide 
 it in their own code.  It certainly isn't going to happen in 
 every case, but if the needed functionality is provided by 
 mixins, then users of the library could have problems.
I spent a while trying to figure out how to automatically verify that a mixin required by a class hierarchy is included in all derived classes, and failed to come up with a general purpose solution. (I found a partial solution involving a `this T` templatized constructor, but it had a lot of problems.) So, I agree now that this is a problem worth solving. It could be fixed by extending template mixins as you and others have proposed. Another possible solution would be to fix `this T` template functions to be virtual, and automatically generate an override for each derived class. This would directly solve the most common use case, and also provide an (awkward) way to automatically verify mixin inclusion in derived classes for more complex scenarios.
templatized functions in classes will never be virtual and there's a technical reason explained here: https://issues.dlang.org/show_bug.cgi?id=1657#c1 (TL;DR: it's problematic to generate the vtbl) In some cases, remixing a template in each sub classes works (for example here https://github.com/Mihail-K/witchcraft#caveats), in some cases a template with a template this parameter works but **here** it's true that among the workarounds, none is conform with the OOP principles as noted in this answer: https://forum.dlang.org/post/pacfkkgazpqaodsbqoss forum.dlang.org.
May 02 2016
parent reply tsbockman <thomas.bockman gmail.com> writes:
On Monday, 2 May 2016 at 17:37:14 UTC, Basile B. wrote:
 templatized functions in classes will never be virtual and 
 there's a technical reason explained here:

 https://issues.dlang.org/show_bug.cgi?id=1657#c1

 (TL;DR: it's problematic to generate the vtbl)
I understand why template methods in general shouldn't be virtual. `this T` is special though, and a method with no other template parameters could be lowered to an ordinary virtual method in a straightforward fashion. Basically, this: class A { string foo(this T)() { return T.stringof; } } class B : A { } Could be lowered to this: class A { string foo() { return A.stringof; } } class B : A { override string foo() { return B.stringof; } } That's not how it works currently, but there is no technical reason it couldn't be *made* to work that way. Obviously this would be a breaking change though; `this T` template methods that should retain the current behaviour could be marked `final`. (Given that non-template methods are virtual by default, I think requiring template methods to be marked `final` would improve code clarity, anyway.)
May 02 2016
parent reply Basile B. <b2.temp gmx.com> writes:
On Monday, 2 May 2016 at 17:50:50 UTC, tsbockman wrote:
 On Monday, 2 May 2016 at 17:37:14 UTC, Basile B. wrote:
 templatized functions in classes will never be virtual and 
 there's a technical reason explained here:

 https://issues.dlang.org/show_bug.cgi?id=1657#c1

 (TL;DR: it's problematic to generate the vtbl)
[...] That's not how it works currently, but there is no technical reason it couldn't be *made* to work that way. Obviously this would be a breaking change though; `this T` template methods that should retain the current behaviour could be marked `final`. (Given that non-template methods are virtual by default, I think requiring template methods to be marked `final` would improve code clarity, anyway.)
Not that much a breaking change. Perhaps a slight decrease of the performances...and even not indeed: in all the previous usages devirtualization of the will be possible.
May 02 2016
parent tsbockman <thomas.bockman gmail.com> writes:
On Monday, 2 May 2016 at 18:14:29 UTC, Basile B. wrote:
 On Monday, 2 May 2016 at 17:50:50 UTC, tsbockman wrote:
 [...]

 That's not how it works currently, but there is no technical 
 reason it couldn't be *made* to work that way.

 Obviously this would be a breaking change though; `this T` 
 template methods that should retain the current behaviour 
 could be marked `final`. (Given that non-template methods are 
 virtual by default, I think requiring template methods to be 
 marked `final` would improve code clarity, anyway.)
Not that much a breaking change. Perhaps a slight decrease of the performances...and even not indeed: in all the previous usages devirtualization of the will be possible.
It's a significant breaking change, because the purpose of it is to change the output of Andrew Benton's earlier example: void main() { Base a = new Base(), b = new Inheritor(); a.writeName.writeln; b.writeName.writeln; // The semantics of this line changes. } Currently, the above will call the `Base` version of `writeName()` for `b`. With my proposal, it would call the `Inheritor()` version.
May 02 2016
prev sibling parent Daniel N <ufo orbiting.us> writes:
On Friday, 29 April 2016 at 10:59:20 UTC, Timon Gehr wrote:
 The use case isn't rare (it is a quite popular request, at 
 least four independent instances that I remember).

 Having a mixin in all classes in some subtree of the class 
 hierarchy can be a good idea. Of course one can easily do it 
 manually, but it is always nice to get rid of boilerplate.
The argument in favour of this feature is even stronger, it also impacts correctness. Both when providing libraries or a large team working within the same framework. Programmer 1: class ComplexAndGenericAbstractBase { ... } Programmer 2: class Car : ComplexAndGenericAbstractBase { ... correctly uses mixin } Programmer 3: class BlueCar : Car { ... no mixin, everything compiles, but is terribly broken at _runtime_(!) } So either P2 needs to remember to prevent anyone from inheriting from 'Car', or P3 needs to type mixin "out of the blue", P2 might not even have documented the need to do so, after all the full doc is in ComplexAndGenericAbstractBase...
Apr 30 2016
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Thursday, 28 April 2016 at 10:21:34 UTC, Andrew Benton wrote:
 I'm running into a set of problems where both inheritance and 
 mixin-added functionality really lend a hand to my project.  
 Unfortunately, they don't necessarily work well together, as 
 certain mixins need to be restated for each inheritor.

 As a toy example, if I wanted to mixin some functionality to 
 write names:

 mixin template WriteName()
 {
     string writeName()
     {
         import std.uuid : randomUUID;
         static if(__traits(hasMember, this, "name"))
             return __traits(getMember, this, "name);
         else
             return randomUUID.toString;
     }
 }
 class Base
 {
     mixin WriteName;
 }
 class Inheritor : Base
 {
     version(both) mixin WriteName;
     string name = "test";
 }

 In the above example, unless I used version "both", I would see 
 only random UUIDs from instances of Base and Inheritor, however 
 the functionality that I really want is to get random UUIDs 
 only from Base, and get "test" from Inheritor.  So far as I 
 know, the easiest way to fix this is to add that mixin to each 
 descendant of Base.

 I recognize that, at least for this toy example, an easier fix 
 is to use a templated function, however I'm more interested in 
 libraries like jsonizer and witchcraft.

 So to the point: Is there an easier way to do this that I'm 
 missing?  Is there a language-design reason that mixed in 
 templates can't inherit?  It seems like an intuitive use of 
 mixin.
This is a common error with mixin template. The static things gets only evaluated once. If you want to be evaluated in each class type you need a true template that get an instance in each class. However the method then **wont be virtual** !! ---- mixin template WriteName() { string writeName(this T)() { import std.uuid : randomUUID; static if (__traits(hasMember, T, "name")) return __traits(getMember, cast(T)this, "name"); else return randomUUID.toString; } } class Base { mixin WriteName; } class Inheritor : Base { string name = "test"; } void main() { import std.stdio; writeln((new Base).writeName!Base); // an UUID writeln((new Inheritor).writeName!Inheritor); // test } ---- see template this parameter: https://dlang.org/spec/template.html#TemplateThisParameter
Apr 28 2016
parent Basile B. <b2.temp gmx.com> writes:
On Thursday, 28 April 2016 at 12:41:57 UTC, Basile B. wrote:
 On Thursday, 28 April 2016 at 10:21:34 UTC, Andrew Benton wrote:
 [...]
This is a common error with mixin template. The static things gets only evaluated once. If you want to be evaluated in each class type you need a true template that get an instance in each class. However the method then **wont be virtual** !! [...]
Oops actually, type is infered: void main() { import std.stdio; writeln((new Base).writeName); // an UUID writeln((new Inheritor).writeName); // test }
Apr 28 2016