www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Implementing interfaces using alias this

reply Balagopal Komarath <baluks gmail.com> writes:
Why doesn't this work? The Test!Duck type has a void quack() 
method but the compiler says it is not implemented.

import std.stdio;

interface IDuck
{
     void quack();
}

class Test(T) : IDuck
{
     T data;
     alias data this;
}

struct Duck
{
     void quack()
     {
         writeln("Quack");
     }
}


void main()
{
	Test!Duck d;
}
Jun 14 2017
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
Balagopal Komarath wrote:

 Why doesn't this work? The Test!Duck type has a void quack() method but 
 the compiler says it is not implemented.
'cause `alias this` is *not* a tool that can be used to emulate inheritance. no, `quack` is NOT impemented. `alias this` won't automagically paste the code.
Jun 14 2017
next sibling parent reply Balagopal Komarath <baluks gmail.com> writes:
On Wednesday, 14 June 2017 at 09:41:49 UTC, ketmar wrote:
 Balagopal Komarath wrote:

 Why doesn't this work? The Test!Duck type has a void quack() 
 method but the compiler says it is not implemented.
'cause `alias this` is *not* a tool that can be used to emulate inheritance. no, `quack` is NOT impemented. `alias this` won't automagically paste the code.
Thanks for the reply. Is there any reason for disallowing this? AFAIK, the alias this guarantees that the interface provided by Test!T is a superset of the interface provided by T. And, when T = Duck, T provides everything required by IDuck. Couldn't the compiler check while instantiating that Test!Duck provides all methods required by IDuck?
Jun 14 2017
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
Balagopal Komarath wrote:

 On Wednesday, 14 June 2017 at 09:41:49 UTC, ketmar wrote:
 Balagopal Komarath wrote:

 Why doesn't this work? The Test!Duck type has a void quack() method but 
 the compiler says it is not implemented.
'cause `alias this` is *not* a tool that can be used to emulate inheritance. no, `quack` is NOT impemented. `alias this` won't automagically paste the code.
Thanks for the reply. Is there any reason for disallowing this? AFAIK, the alias this guarantees that the interface provided by Test!T is a superset of the interface provided by T. And, when T = Duck, T provides everything required by IDuck. Couldn't the compiler check while instantiating that Test!Duck provides all methods required by IDuck?
besides improper using of `alias this`, there is a technical reason too: `struct` is not a `class`, it doesn't have VMT, and other class goodies. as i said, `alias this` is not a macro, it doesn't paste code, it just tells the compiler to make "symbol redirection" on typechecking. interfaces *require* a full-featured class, with VMT, and some hidden pointers to support hidden interface machinery.
Jun 14 2017
parent reply Balagopal Komarath <baluks gmail.com> writes:
On Wednesday, 14 June 2017 at 11:40:02 UTC, ketmar wrote:
 interfaces *require* a full-featured class, with VMT, and some 
 hidden pointers to support hidden interface machinery.
I don't think that is the problem here. The type Test!Duck is a class and that type is the one implementing the interface.
Jun 14 2017
parent ketmar <ketmar ketmar.no-ip.org> writes:
Balagopal Komarath wrote:

 On Wednesday, 14 June 2017 at 11:40:02 UTC, ketmar wrote:
 interfaces *require* a full-featured class, with VMT, and some hidden 
 pointers to support hidden interface machinery.
I don't think that is the problem here. The type Test!Duck is a class and that type is the one implementing the interface.
*sighs*
Jun 14 2017
prev sibling parent reply Mike B Johnson <Mikey Ikes.com> writes:
On Wednesday, 14 June 2017 at 09:41:49 UTC, ketmar wrote:
 Balagopal Komarath wrote:

 Why doesn't this work? The Test!Duck type has a void quack() 
 method but the compiler says it is not implemented.
'cause `alias this` is *not* a tool that can be used to emulate inheritance. no, `quack` is NOT impemented. `alias this` won't automagically paste the code.
I don't think it has to do with pasting code. d.Quack() is well defined through the alias. Inheritance requires that a Quack() exists, and it does, through the alias. The compiler could easily create an implementation wrapper that uses the alias this. I believe it is simply because the logic is not implement in the compiler, not because there is some logic issue in it. import std.stdio; interface IDuck { void quack(); } class Test(T) : IDuck { T data; alias data this; void quack() { data.quack(); } } struct Duck { void quack() { writeln("Quack"); } } void main() { Test!Duck d; d.quack(); } which, unfortunately causes a segmentation fault ;)(from dpaste) (even if you remove IDuck) https://dpaste.dzfl.pl/69ddb3f2b1e9 The main issue is that the compiler would have to parse the alias and see if it has members that are to be used, not hard... but this is probably not standard behavior and might lead to other problems down the road(specially with multiple alias this). Basically, which quack to call? I think it's better to require it to be explicit(as I have tried to do).
Jun 14 2017
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
Mike B Johnson wrote:

 I don't think it has to do with pasting code.

 d.Quack() is well defined through the alias. Inheritance requires that a 
 Quack() exists, and it does, through the alias.

 The compiler could easily create an implementation wrapper that uses the 
 alias this.
this is called "code pasting".
Jun 14 2017
prev sibling parent Balagopal Komarath <baluks gmail.com> writes:
On Wednesday, 14 June 2017 at 12:35:05 UTC, Mike B Johnson wrote:
 void main()
 {
 	Test!Duck d;
 	d.quack();
 }

 which, unfortunately causes a segmentation fault ;)
I think that is because you are not initializing d using new Test!Duck();
Jun 14 2017
prev sibling next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/14/17 5:34 AM, Balagopal Komarath wrote:
 Why doesn't this work? The Test!Duck type has a void quack() method but
 the compiler says it is not implemented.

 import std.stdio;

 interface IDuck
 {
     void quack();
 }

 class Test(T) : IDuck
 {
     T data;
     alias data this;
 }

 struct Duck
 {
     void quack()
     {
         writeln("Quack");
     }
 }


 void main()
 {
     Test!Duck d;
 }
An alias never causes implementation to be emitted. It should just be a forward from one symbol name to another. You need an actual function that can go into the vtable that takes a 'Test!Duck' as the context pointer. This wrapper has to be written by you. -Steve
Jun 14 2017
prev sibling next sibling parent reply basile b. <b2.temp gmx.com> writes:
On Wednesday, 14 June 2017 at 09:34:27 UTC, Balagopal Komarath 
wrote:
 Why doesn't this work? The Test!Duck type has a void quack() 
 method but the compiler says it is not implemented.

 import std.stdio;

 interface IDuck
 {
     void quack();
 }

 class Test(T) : IDuck
 {
     T data;
     alias data this;
 }

 struct Duck
 {
     void quack()
     {
         writeln("Quack");
     }
 }


 void main()
 {
 	Test!Duck d;
 }
The way to do that in D is with mixins: interface IDuck { void quack(); } class Test(alias MyImpl) : IDuck { mixin MyImpl; } mixin template DuckImpl() { void quack() { import std.stdio; writeln("Quack Quack"); } } void main() { (new Test!DuckImpl).quack; }
Jun 14 2017
parent reply Balagopal Komarath <baluks gmail.com> writes:
On Wednesday, 14 June 2017 at 21:04:55 UTC, basile b. wrote:
 The way to do that in D is with mixins:
That is an interesting solution. However, my original goal was to figure out whether one can make struct types behave polymorphically (Which is not mentioned in my original question). I know that this is not supported by design. I want to keep the original struct types but also allow functions to accept those struct types and do runtime dispatch. The following code should give a better idea by what I mean by this. https://dpaste.dzfl.pl/051f6a4da059 The user need only define Duck1, Duck2 ... with appropriate functions and mixin toDuck. Then a function that accepts a Duck can accept DuckLike (for dynamic dispatch) or templatize on the type of Duck.
Jun 14 2017
parent basile b. <b2.temp gmx.com> writes:
On Wednesday, 14 June 2017 at 22:29:08 UTC, Balagopal Komarath 
wrote:
 On Wednesday, 14 June 2017 at 21:04:55 UTC, basile b. wrote:
 The way to do that in D is with mixins:
That is an interesting solution. However, my original goal was to figure out whether one can make struct types behave polymorphically (Which is not mentioned in my original question). I know that this is not supported by design. I want to keep the original struct types but also allow functions to accept those struct types and do runtime dispatch. The following code should give a better idea by what I mean by this. https://dpaste.dzfl.pl/051f6a4da059 The user need only define Duck1, Duck2 ... with appropriate functions and mixin toDuck. Then a function that accepts a Duck can accept DuckLike (for dynamic dispatch) or templatize on the type of Duck.
Duck typing doesn't work with the pattern you used initially. The pattern you used initially is much more similar to the solution i exposed. The point is that "alias" cant do what you wished. It's not in the language, end of story. Finally duck types in D are more like this: enum looksLikeaDuck(T) = __traits(hasMember, T, "quack"); class Test(T) if (looksLikeaDuck!T) { T data; alias data this; } struct Duck { void quack() { import std.stdio; writeln("Quack"); } } void main() { Test!Duck d; } you use traits to verify that the template parameter looks like a duck. It's not like in Swift where an aggregate is used. In D we use traits (even if traits can be used to verify that a template parameter is compliant with the member of an aggregate).
Jun 14 2017
prev sibling next sibling parent reply Biotronic <simen.kjaras gmail.com> writes:
On Wednesday, 14 June 2017 at 09:34:27 UTC, Balagopal Komarath 
wrote:
 void main()
 {
 	Test!Duck d;
 }
As has been pointed out at length by others here, it's simply not how alias this is intended to work. I do see some arguments in favor of working that way, but I'm not sure what's the right solution. Anyways, there is another solution - we could write a template that does the conversion for us. There is std.typecons.Proxy, which seems like a good fit, however it doesn't work quite the way we want. Here however, is a solution that works for simple examples. It might work for more complex examples as well, but I simply haven't tested that: template duckImpl(alias a, TI, Fns...) { static if (Fns.length > 0) { mixin duckImpl!(a, TI, Fns[1..$]); alias thisFn = Fns[0]; enum string fnName = __traits(identifier, thisFn); mixin("ReturnType!thisFn "~fnName~"(Parameters!thisFn args) { return a."~fnName~"(args); }"); } } template duck(TI) if (is(TI == interface)) { TI duck(T)(T t) { import std.meta; import std.traits; template Functions(string s) { alias Functions = MemberFunctionsTuple!(TI, s); } static class Result : TI { private T payload; this(T value) { payload = value; } mixin duckImpl!(payload, TI, staticMap!(Functions, __traits(allMembers, TI))); } return new Result(t); } } interface I { void bar(); } struct S { void bar() { import std.stdio; writeln("OHAI"); } } unittest { I i = S().duck!I; i.bar(); } -- Biotronic
Jun 15 2017
parent Balagopal Komarath <baluks gmail.com> writes:
On Thursday, 15 June 2017 at 07:12:56 UTC, Biotronic wrote:
 Here however, is a solution that works for simple examples.
This is awesome. Very generic. Thanks.
Jun 15 2017
prev sibling parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
On Wednesday, 14 June 2017 at 09:34:27 UTC, Balagopal Komarath 
wrote:
 Why doesn't this work? The Test!Duck type has a void quack() 
 method but the compiler says it is not implemented.
You question was answered, but you can do this: ------------------ interface IDuck { void quack(); } struct Duck { void quack() { import std.stdio : writeln; writeln("Quack"); } } void main() { Duck d; import std.experimental.typecons : wrap; IDuck i = wrap!IDuck(d); i.quack(); } ----------------- Please note that you can't wrap to an interface which returns itself interface IDuck { IDuck clone(); } It fails because the struct would return a Duck and wouldn't wrap it to an IDuck. This is only a limitation on wrap because I haven't written the detection and appropriate wrapping function call.
Jun 15 2017
parent reply Biotronic <simen.kjaras gmail.com> writes:
On Thursday, 15 June 2017 at 18:49:58 UTC, Jesse Phillips wrote:
 wrap!IDuck
Ah, so it does exist in Phobos. I thought it should be there, but didn't find it. Thanks! -- Biotronic
Jun 16 2017
parent Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
On Friday, 16 June 2017 at 08:34:21 UTC, Biotronic wrote:
 On Thursday, 15 June 2017 at 18:49:58 UTC, Jesse Phillips wrote:
 wrap!IDuck
Ah, so it does exist in Phobos. I thought it should be there, but didn't find it. Thanks! -- Biotronic
Yeah, when Andrei introduced the wrap function I was disappointed it only wrapped classes. But in truth I haven't really been using it for structs either. That may partly be because it can't wrap to the range interface.
Jun 16 2017