Multiple subtyping with alias this and nested classes

Yigal Chripun yigal100 at gmail.com
Mon Oct 5 15:52:42 PDT 2009


On 05/10/2009 12:19, Max Samukha wrote:
> On Sun, 04 Oct 2009 12:31:08 -0400, Yigal Chripun<yigal100 at gmail.com>
> wrote:
>
>> Max Samukha Wrote:
>>>>
>>>> I see. What you want is non-virtual MI.
>>>> I don't think that allowing the derived class to have two distinct implementations for the same function prototype is a good idea.
>>>> Eiffel handles this by giving the programmer controls to select features and rename them in the derived class which I think is better.
>>>>
>>>> // hypothetical syntax example
>>>> class FlippingBlipper : IBlipper, IFilpper {
>>>>      mixin Flipper F;
>>>>      mixin Blipper B;
>>>>      // rename the inherited interface functions
>>>>      alias IBlipper.nameCollision boo;
>>>>      alias IFilpper.nameCollision foo;
>>>>     // implement the now two distinct functions
>>>>    void foo() { F.nameCollision(); }
>>>>    void boo() { B.nameCollision(); }
>>>> }
>>>>
>>>> you can further subtype and the derived foo&  boo will override the respective interfaces.
>>>
>>> Yes, but what if nameCollision is a virtual function called somewhere
>>> in the template mixin? Your example has effectively removed the
>>> virtuality.
>>>
>>
>> it doesn't remove virtuality. Virtual functions are stored as pointers in the vtable. with my suggested solution the idea is that the same pointer in the vtable gets a new name in the derived class.
>> when you mixin the template into the derived class the compiler would  resolve nameCollision to the new name "foo" because of the renaming rule in the derived class.
>
> Ok, I still cannot see how the compiler can reliably determine that
> IFlipper.nameCollision is implemented by Flipper and not by Blipper
> and call the correct override when flip is called.
>
> Should the compiler deduce that from F.nameCollision? If yes, what
> about:
> void foo() { B.nameCollision; F.nameCollision; } ?
>
> Or should it decide based on the fact that Flipper contains the 'flip'
> function that implements IFlipper.flip? If yes, then what if the
> interfaces had nothing more than the 'nameCollision' functions?
>
I see you want to discuss the details of the renaming mechanism, while I 
just talked about the general idea itself until now.

we have two cases: regular and mixins.
for case a it's simple, the class itself lists the renames:

Interface A {
void foo();
}
Interface B {
void foo();
}

class C : A, B {
alias A.foo func1; // or similar syntax to mark the renaming
alias B.foo func2; // ditto

override void func1() {...} // C.func1 overrides A.foo
override void func2() {...} // C.func2 overrides B.foo
}

case 2, mixins:
I think some sort of annotations by the programmer is needed.

class C : A, B {
alias A.foo func1; // or similar syntax to mark the renaming
alias B.foo func2; // ditto

// hypothetical syntax to connect mixins to interfaces
// the compiler will apply the rename rules of this class assuming that
// any references to foo in ImplA are "A.foo" and will be renamed to
// func1 in C
// if the mixin explicitly specifies the interface (e.g B.foo) than the 
// compiler already knows what to map this to.
mixin ImplA A;
mixin ImplB B;
}

btw, this all can be done in a much simpler way with (hygienic) AST Macros.

>> I don't think I want non virtual MI in D since it is more trouble than it's worth. but *if* we discuss this, I think my suggested semantics are simpler. The syntax probably can be much better but that's a secondary issue IMO.
>>
>
> It's unrelated to virtual MI. See Sergey Gromov's post. We are
> discussing this only because it's a real world problem that has no
> decent solution in D.
>

I said *non* virtual MI above..

>>
>> with my design:
>> class Foo : FlippingBlipper {
>> override foo ...
>> override bar ...
>> }
>> IFlipper obj = new Foo;
>> obj.nameCollision; // will call Foo.bar
>
>>
>> I don't think that's possible with your design.
>
> I'm not claiming it's my design. Andrei is the author.
>
> And yes, it's possible in a number of ways. One is:
>
> class BlippingFlipper
> {
>     class Flipper_ : Flipper
>     {
>       override void nameCollision()  {  foo(); }
>       final void super_nameCollision() { super.nameCollision;  }
>     }
>
>     this() { flipper = new Flipper_; }
>
>     Flipper_ flipper;
>     alias flipper this;
>
>     void foo() { flipper.super_nameCollision; }
> }
>
> class Foo : BlippingFlipper
> {
>      override void foo() {}
> }
>
> (Wordy but still possible. Can be made easier on the eye with a helper
> mixin)

This gets more and more complicated - Like I said before, I feel that 
renaming has easier to understand semantics.
>
> I agree that possibility to implement renamed interface methods AND
> explicit interface implementation would solve the problem of name
> collisions. But it doesn't solve the problem of subtyping structs and
> built-in types.
>

the problem with explicit interface implementation as discussed in 
another thread is that the implementations will become hidden.

class C: A, B {
override void A.foo () { ... }
override void B.foo () { ... }
}

C obj = new C;
obj.foo(); // error

class D : C { ... }
what happens in class D? Can I override the functions there as well? how?




you do not need explicit interface implementation at all if you have 
renaming.

>>
>> besides, alias this is a hack. a better mechanism would be to have compile-time inheritance, IMO.
>
> I think 'alias this' is a powerful feature.
>
> Given the constraints like "easy for the compiler writer to implement"
> and "we have many other things to do", I doubt traits or something
> like that will be or needs to be in the language.

Alias this is powerful black magic. being powerful doesn't make it any 
less dark.



More information about the Digitalmars-d mailing list