www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - mixin bug?

reply Engine Machine <EM EM.com> writes:
template F1(T)
{
    void bar() { writeln("Bar0"); }
}

template F2(T)
{
    mixin F1!T;
    void foo() { bar(); }
}

template F3(T)
{
    mixin F2!T;
    void bar() { writeln("Bar1"); } // <- This bar should be used 
for F2's foo!

}

struct F4(T)
{
     mixin F3!T;
}

(Or on can turn F3 in to a struct directly)


Then f3.foo() calls bar from F0, not F3's bar! This seems like a 
big bug! One expects the same behavior of mixins regardless of 
mixin nesting.

While you could argue that foo, when declared, is calling F0's 
bar, this is not consistent with the view that mixin templates 
only adds what is not there. I don't like the idea that calls are 
resolved first come first serve as it means one can't extend 
templates in a natural logical way.
Aug 11 2016
parent reply sldkf <sldkf sldkf.fr> writes:
On Thursday, 11 August 2016 at 17:56:47 UTC, Engine Machine wrote:
 template F1(T)
 {
    void bar() { writeln("Bar0"); }
 }

 template F2(T)
 {
    mixin F1!T;
    void foo() { bar(); }
 }

 template F3(T)
 {
    mixin F2!T;
    void bar() { writeln("Bar1"); } // <- This bar should be 
 used for F2's foo!

 }

 struct F4(T)
 {
     mixin F3!T;
 }

 (Or on can turn F3 in to a struct directly)


 Then f3.foo() calls bar from F0, not F3's bar! This seems like 
 a big bug! One expects the same behavior of mixins regardless 
 of mixin nesting.

 While you could argue that foo, when declared, is calling F0's 
 bar, this is not consistent with the view that mixin templates 
 only adds what is not there. I don't like the idea that calls 
 are resolved first come first serve as it means one can't 
 extend templates in a natural logical way.
I don't think it's a bug. F3's bar() doesn't exist yet in F2. Logically F1's one is called. The language allows to alias a mixin so that a particular overload can be called. In you case you can really target F1.bar without problem: °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° template F1(T){void bar() {writeln("Bar0");}} template F2(T) { mixin F1!T FF1; void foo() { FF3.bar; } } template F3(T) { mixin F2!T FF2; void bar() { writeln("Bar1");} } struct F4(T){mixin F3!T FF3;} °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° See https://dlang.org/spec/template-mixin.html#mixin_scope However the specification doesn't say what is the rule that's applied. Even if it look logical that F1.bar() is chosen it has to be written somewhere.
Aug 11 2016
parent reply Engine Machine <EM EM.com> writes:
On Thursday, 11 August 2016 at 19:05:58 UTC, sldkf wrote:
 On Thursday, 11 August 2016 at 17:56:47 UTC, Engine Machine 
 wrote:
 template F1(T)
 {
    void bar() { writeln("Bar0"); }
 }

 template F2(T)
 {
    mixin F1!T;
    void foo() { bar(); }
 }

 template F3(T)
 {
    mixin F2!T;
    void bar() { writeln("Bar1"); } // <- This bar should be 
 used for F2's foo!

 }

 struct F4(T)
 {
     mixin F3!T;
 }

 (Or on can turn F3 in to a struct directly)


 Then f3.foo() calls bar from F0, not F3's bar! This seems like 
 a big bug! One expects the same behavior of mixins regardless 
 of mixin nesting.

 While you could argue that foo, when declared, is calling F0's 
 bar, this is not consistent with the view that mixin templates 
 only adds what is not there. I don't like the idea that calls 
 are resolved first come first serve as it means one can't 
 extend templates in a natural logical way.
I don't think it's a bug. F3's bar() doesn't exist yet in F2. Logically F1's one is called.
Yes, but when we "mixin" again, bar then does exist. I see the mixin as a sort of copy and paste. We only paste in what doesn't exist. So the first time bar gets inserted in to F2, but then that bar doesn't get inserted in to F3 because it already exists. Basically a mixin of a mixin is not logically a mixin. That makes no sense to me. A mixin is suppose to behave a certain way, but when we do a mixin of a mixin we get behavior that doesn't behave the same way as a single mixin.
 The language allows to alias a mixin so that a particular 
 overload can be called. In you case you can really target 
 F1.bar without problem:
I don't want to target F1.bar, I want foo to target future bar's. It is like overloading, but for templates. If you were doing overloading, and it called the base function instead, it wouldn't be overloading, would it?
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 template F1(T){void bar() {writeln("Bar0");}}

 template F2(T)
 {
    mixin F1!T FF1;
    void foo() { FF3.bar; }
 }
This requires F2 to know the future. It also forces it to use a specific bar. I want inheritance like logic. Which is what we get when we mixin one deep, but when we go deeper, it breaks. I think this is a bug. It seems like D is trying to resolve things only after each mixin, rather than resolving after all nested mixins are evaluated.
Aug 11 2016
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 08/11/2016 01:27 PM, Engine Machine wrote:

 I see the mixin as a sort of copy and paste.
That's the case for string mixins. Template mixins bring a name resolution scope. My understanding of the topic: http://ddili.org/ders/d.en/mixin.html#ix_mixin.name%20space,%20mixin The spec: https://dlang.org/spec/template-mixin.html Ali
Aug 11 2016
parent Engine Machine <EM EM.com> writes:
On Thursday, 11 August 2016 at 21:03:36 UTC, Ali Çehreli wrote:
 On 08/11/2016 01:27 PM, Engine Machine wrote:

 I see the mixin as a sort of copy and paste.
That's the case for string mixins. Template mixins bring a name resolution scope. My understanding of the topic: http://ddili.org/ders/d.en/mixin.html#ix_mixin.name%20space,%20mixin The spec: https://dlang.org/spec/template-mixin.html Ali
"When a mixed-in name is the same as a name that is in the surrounding scope, then the name that is in the surrounding scope gets used:" Which is my point, for "2nd order or higher" mixins, this does not occur. The why I see it is that mixin template is sort of like a copy and paste, but only paste if the members do not exist already(i.e., the current behavior for "1st order" mixins. I would expect the same logic to hold for higher order mixins since a mixin of a mixin is still a mixin). I realize why D behaves the way it does, but this is limiting. It simply greedily resolves referencing members rather than lazily. If the greedy method actually has some desired characteristic, then a lazy mixin would be nice. lazy mixin T1!T;
Aug 11 2016
prev sibling parent reply sldkf <sldkf sldkf.fr> writes:
On Thursday, 11 August 2016 at 20:27:01 UTC, Engine Machine wrote:
 This requires F2 to know the future. It also forces it to use a 
 specific bar. I want inheritance like logic.
You are goind to hit a wall. Template programming is not OOP. I'm not even sure that reflection would work in order to determine the most "recent" overload.
 Which is what we get when we mixin one deep, but when we go 
 deeper, it breaks. I think this is a bug. It seems like D is 
 trying to resolve things only after each mixin, rather than 
 resolving after all nested mixins are evaluated.
This is the only issue I see: "It seems like D is trying to...". We need to know exactly what D does: https://issues.dlang.org/show_bug.cgi?id=16376.
Aug 11 2016
parent reply Engine Machine <EM EM.com> writes:
On Thursday, 11 August 2016 at 21:25:20 UTC, sldkf wrote:
 On Thursday, 11 August 2016 at 20:27:01 UTC, Engine Machine 
 wrote:
 This requires F2 to know the future. It also forces it to use 
 a specific bar. I want inheritance like logic.
You are goind to hit a wall. Template programming is not OOP. I'm not even sure that reflection would work in order to determine the most "recent" overload.
Well, duh, it is not oop, but that doesn't mean it doesn't have similar abstractions, you need to read between the lines a bit more.
 Which is what we get when we mixin one deep, but when we go 
 deeper, it breaks. I think this is a bug. It seems like D is 
 trying to resolve things only after each mixin, rather than 
 resolving after all nested mixins are evaluated.
This is the only issue I see: "It seems like D is trying to...". We need to know exactly what D does: https://issues.dlang.org/show_bug.cgi?id=16376.
It would be nice if D would bind the function calls lazily, so to speak, that is all I'm saying. This way we can get polymorphic/oop like behavior, if you will. template A(T) { void Bark() { writeln("Ruff Ruff"); } } template B(T) { mixin A!T; void Talk() { Bark(); } } template Duck(T) { mixin B!T; private void Bark() { writeln("Quack"); } } this is a contrived and misleading example if you take it seriously. But Duck.Talk(); should Quack. It would if D resolved Bark from Talk after the final "mixin". (In Duck, not in B). Else we are stuck with a barking duck. What it seems to do is first evaluate the template A!T, then B!T, then Duck!T. Since B!T is evaluated first and plugged in to Duck, Talk is already resolved to use A!T.Bark. Rather, If Duck!T was evaluated first, D would understand that Bark was already defined and when Talk was added, it could have it use the bark defined in duck, rather than A. This, would, of course, require a sort of Duck!T.B!T type, since B!T used inside of Duck would have a different Talk() than if it is not used in Duck. Again, maybe the compiler is just to ignorant to do this as it is complex(might require flow analysis and all that stuff or just be an ill-defined problem in general).
Aug 11 2016
parent reply sldkf <sldkf sldkf.fr> writes:
On Friday, 12 August 2016 at 02:09:21 UTC, Engine Machine wrote:
 On Thursday, 11 August 2016 at 21:25:20 UTC, sldkf wrote:
 On Thursday, 11 August 2016 at 20:27:01 UTC, Engine Machine
issue solved using a "template this parameter": °°°°°°°°°°°°°°°°°°°°°°°°°°°°°° template Cow() { void soundImpl() { writeln("moo"); } } template Cat() { mixin AnimalSound; void soundImpl() { writeln("meaow"); } } template Duck() { mixin Cat; void soundImpl() { writeln("quack"); } } template AnimalSound() { void emittSound(this T)() { (cast(T) this).soundImpl(); } // would also work with "this.soundImpl()" } struct Animal { mixin Duck; } void main() { Animal a; a.emittSound; } °°°°°°°°°°°°°°°°°°°°°°°°°°°°°° https://dlang.org/spec/template.html#TemplateThisParameter
Aug 12 2016
parent reply Engine Machine <EM EM.com> writes:
On Friday, 12 August 2016 at 15:35:50 UTC, sldkf wrote:
 On Friday, 12 August 2016 at 02:09:21 UTC, Engine Machine wrote:
 On Thursday, 11 August 2016 at 21:25:20 UTC, sldkf wrote:
 On Thursday, 11 August 2016 at 20:27:01 UTC, Engine Machine
issue solved using a "template this parameter": °°°°°°°°°°°°°°°°°°°°°°°°°°°°°° template Cow() { void soundImpl() { writeln("moo"); } } template Cat() { mixin AnimalSound; void soundImpl() { writeln("meaow"); } } template Duck() { mixin Cat; void soundImpl() { writeln("quack"); } } template AnimalSound() { void emittSound(this T)() { (cast(T) this).soundImpl(); } // would also work with "this.soundImpl()" } struct Animal { mixin Duck; } void main() { Animal a; a.emittSound; } °°°°°°°°°°°°°°°°°°°°°°°°°°°°°° https://dlang.org/spec/template.html#TemplateThisParameter
This is not the solution to the original problem. template X(T) { int _len = 0; int Length() { return _len; } int length(this T)() { return (cast(T)this).Length(); } } template Y(T) { mixin X!T; int Length() { return 3; } } Then calling length returns 0, while calling Length returns 3. But we obviously want length to "follow" Length, yet it doesn't due to D resolving length in X before it is mixed in Y!T.
Aug 12 2016
parent reply sldkf <sldkf sldkf.fr> writes:
On Friday, 12 August 2016 at 23:14:23 UTC, Engine Machine wrote:
 On Friday, 12 August 2016 at 15:35:50 UTC, sldkf wrote:
 On Friday, 12 August 2016 at 02:09:21 UTC, Engine Machine 
 wrote:
 On Thursday, 11 August 2016 at 21:25:20 UTC, sldkf wrote:
 On Thursday, 11 August 2016 at 20:27:01 UTC, Engine Machine
This is not the solution to the original problem. template X(T) { int _len = 0; int Length() { return _len; } int length(this T)() { return (cast(T)this).Length(); } } template Y(T) { mixin X!T; int Length() { return 3; } } Then calling length returns 0, while calling Length returns 3. But we obviously want length to "follow" Length, yet it doesn't due to D resolving length in X before it is mixed in Y!T.
No "length" returns 3. https://ideone.com/T8HjZl Really, it's there, in front of your eyes.
Aug 12 2016
parent Engine Machine <EM EM.com> writes:
On Friday, 12 August 2016 at 23:48:54 UTC, sldkf wrote:
 On Friday, 12 August 2016 at 23:14:23 UTC, Engine Machine wrote:
 On Friday, 12 August 2016 at 15:35:50 UTC, sldkf wrote:
 On Friday, 12 August 2016 at 02:09:21 UTC, Engine Machine 
 wrote:
 On Thursday, 11 August 2016 at 21:25:20 UTC, sldkf wrote:
 On Thursday, 11 August 2016 at 20:27:01 UTC, Engine Machine
This is not the solution to the original problem. template X(T) { int _len = 0; int Length() { return _len; } int length(this T)() { return (cast(T)this).Length(); } } template Y(T) { mixin X!T; int Length() { return 3; } } Then calling length returns 0, while calling Length returns 3. But we obviously want length to "follow" Length, yet it doesn't due to D resolving length in X before it is mixed in Y!T.
No "length" returns 3. https://ideone.com/T8HjZl Really, it's there, in front of your eyes.
You're right. My mistake. Thanks! Do I have to do this with every function call? Seems a bit tedious to have to add this T to every function and add this. to every call.
Aug 12 2016