www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - D interface bug?

reply Alex <AJ gmail.com> writes:
interface iBase
{
	iBase fooBase(iBase);
}


class cBase : iBase
{
	cBase fooBase(cBase c) { return c; }	

}

cBase.fooBase should be a valid override of iBase.fooBase because 
  they are the same type! cBase is a super type so it contains 
everything iBase contains and maybe more.

There should be no reason why the compiler can't figure this out. 
It's a very simple rule.

Any time the user calls iBase.fooBase it can be replaced with 
cBase.fooBase so it should not compromise any code to go ahead 
and accept it as a proper override.
Mar 29 2019
next sibling parent destructionator gmail.com writes:
On Friday, 29 March 2019 at 23:44:35 UTC, Alex wrote:
 interface iBase
 {
 	iBase fooBase(iBase);
 }


 class cBase : iBase
 {
 	cBase fooBase(cBase c) { return c; }	

 }

 cBase.fooBase should be a valid override of iBase.fooBase 
 because
  they are the same type!
The return value there is allowed, but the parameter is not. Consider this case: class AnotherChild : iBase { /* snip */ } iBase i = new cBase(); // allowed, cBase implements iBase i.fooBase(new AnotherChild); That second line should be allowed: the interface says it accepts any implementation of the iBase interface. But the cBase implementation doen't allow *any* implementation of iBase - it only accepts cBase and down. That function wouldn't be able to handle my AnotherChild instance. Thus, cBase.fooBase does NOT implement the iBase's interface, and it fails to compile.
Mar 29 2019
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Mar 29, 2019 at 11:44:35PM +0000, Alex via Digitalmars-d-learn wrote:
 interface iBase
 {
 	iBase fooBase(iBase);
 }
 
 
 class cBase : iBase
 {
 	cBase fooBase(cBase c) { return c; }	
 
 }
 
 cBase.fooBase should be a valid override of iBase.fooBase because they
 are the same type! cBase is a super type so it contains everything
 iBase contains and maybe more.
No, that's wrong. Consider this: class cBase : iBase { int x; cBase fooBase(cBase c) { return (x==1) ? c : null; } } class dBase : iBase { string y; dBase fooBase(dBase c) { return (y=="a") ? c : null; } } iBase intf = new cBase; dBase dobj = new dBase; dobj.fooBase(intf); // oops: intf.y doesn't exist! I.e., it's invalid for dBase.fooBase to override the interface method. The parameter type of fooBase must be the interface type or a super-interface thereof. For a class C to inherit from an interface X means that C contains a subset of all possible objects that X might refer to. Therefore, if a method takes a parameter of type C, it *cannot* be passed an argument of type X, since the actual object might be outside the subset that C includes. IOW, such a method cannot be covariant with a method that takes X as a parameter.
 There should be no reason why the compiler can't figure this out. It's
 a very simple rule.
 
 Any time the user calls iBase.fooBase it can be replaced with
 cBase.fooBase so it should not compromise any code to go ahead and
 accept it as a proper override.
[...] Nope. The user can call iBase.fooBase, passing it an instance of a different class that also implements iBase but does not inherit from cBase. Then cBase.fooBase would receive an argument of incompatible type. T -- "A man's wife has more power over him than the state has." -- Ralph Emerson
Mar 29 2019
parent reply Alex <AJ gmail.com> writes:
On Saturday, 30 March 2019 at 00:06:23 UTC, H. S. Teoh wrote:
 On Fri, Mar 29, 2019 at 11:44:35PM +0000, Alex via 
 Digitalmars-d-learn wrote:
 interface iBase
 {
 	iBase fooBase(iBase);
 }
 
 
 class cBase : iBase
 {
 	cBase fooBase(cBase c) { return c; }
 
 }
 
 cBase.fooBase should be a valid override of iBase.fooBase 
 because they are the same type! cBase is a super type so it 
 contains everything iBase contains and maybe more.
No, that's wrong. Consider this: class cBase : iBase { int x; cBase fooBase(cBase c) { return (x==1) ? c : null; } } class dBase : iBase { string y; dBase fooBase(dBase c) { return (y=="a") ? c : null; } } iBase intf = new cBase; dBase dobj = new dBase; dobj.fooBase(intf); // oops: intf.y doesn't exist! I.e., it's invalid for dBase.fooBase to override the interface method. The parameter type of fooBase must be the interface type or a super-interface thereof. For a class C to inherit from an interface X means that C contains a subset of all possible objects that X might refer to. Therefore, if a method takes a parameter of type C, it *cannot* be passed an argument of type X, since the actual object might be outside the subset that C includes. IOW, such a method cannot be covariant with a method that takes X as a parameter.
 There should be no reason why the compiler can't figure this 
 out. It's a very simple rule.
 
 Any time the user calls iBase.fooBase it can be replaced with 
 cBase.fooBase so it should not compromise any code to go ahead 
 and accept it as a proper override.
[...] Nope. The user can call iBase.fooBase, passing it an instance of a different class that also implements iBase but does not inherit from cBase. Then cBase.fooBase would receive an argument of incompatible type. T
Ok. In my use case, which is what I was thinking of, there will never be a dBase. There will never be any other class that inherits from the interface. I have to use an interface ONLY because D does not allow for multiple inheritance. class X; class C; class Q : X, C; Which can't be done, so I want to do interface iC; class C : iC; class Q : X, iC; which now works. The problem now is that I have to then still follow these rules which are very restrictive. It's true that someone could come along and create an new class D : iC and cause problems, but that should never happen in my case. Ideally, if they did, they would use the same pattern as above: interface iD; class D : C, iD; and this then also alleviates the problem. In your case it is iC / \ / \ C D but in my case it should never happen, or if it would, it is better to do iC / / C iD \ / \ / D I'm only using interfaces because I have to, not because I want to. But then that forces me to do strange things in D and it causes many problems. Since one can't have fields in an interface it requires using properties and all that code bloat that comes with them, along with the casting issues, and overloading, etc.
Mar 29 2019
parent Alex <sascha.orlov gmail.com> writes:
On Saturday, 30 March 2019 at 00:44:31 UTC, Alex wrote:
 On Saturday, 30 March 2019 at 00:06:23 UTC, H. S. Teoh wrote:
 On Fri, Mar 29, 2019 at 11:44:35PM +0000, Alex via 
 Digitalmars-d-learn wrote:
 interface iBase
 {
 	iBase fooBase(iBase);
 }
 
 
 class cBase : iBase
 {
 	cBase fooBase(cBase c) { return c; }
 
 }
 
 cBase.fooBase should be a valid override of iBase.fooBase 
 because they are the same type! cBase is a super type so it 
 contains everything iBase contains and maybe more.
No, that's wrong. Consider this: class cBase : iBase { int x; cBase fooBase(cBase c) { return (x==1) ? c : null; } } class dBase : iBase { string y; dBase fooBase(dBase c) { return (y=="a") ? c : null; } } iBase intf = new cBase; dBase dobj = new dBase; dobj.fooBase(intf); // oops: intf.y doesn't exist! I.e., it's invalid for dBase.fooBase to override the interface method. The parameter type of fooBase must be the interface type or a super-interface thereof. For a class C to inherit from an interface X means that C contains a subset of all possible objects that X might refer to. Therefore, if a method takes a parameter of type C, it *cannot* be passed an argument of type X, since the actual object might be outside the subset that C includes. IOW, such a method cannot be covariant with a method that takes X as a parameter.
 There should be no reason why the compiler can't figure this 
 out. It's a very simple rule.
 
 Any time the user calls iBase.fooBase it can be replaced with 
 cBase.fooBase so it should not compromise any code to go 
 ahead and accept it as a proper override.
[...] Nope. The user can call iBase.fooBase, passing it an instance of a different class that also implements iBase but does not inherit from cBase. Then cBase.fooBase would receive an argument of incompatible type. T
Ok. In my use case, which is what I was thinking of, there will never be a dBase. There will never be any other class that inherits from the interface. I have to use an interface ONLY because D does not allow for multiple inheritance. class X; class C; class Q : X, C; Which can't be done, so I want to do interface iC; class C : iC; class Q : X, iC; which now works. The problem now is that I have to then still follow these rules which are very restrictive. It's true that someone could come along and create an new class D : iC and cause problems, but that should never happen in my case. Ideally, if they did, they would use the same pattern as above: interface iD; class D : C, iD; and this then also alleviates the problem. In your case it is iC / \ / \ C D but in my case it should never happen, or if it would, it is better to do iC / / C iD \ / \ / D I'm only using interfaces because I have to, not because I want to. But then that forces me to do strange things in D and it causes many problems. Since one can't have fields in an interface it requires using properties and all that code bloat that comes with them, along with the casting issues, and overloading, etc.
Maybe, CRTP is something you can use? https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern The fact, that your interfaces are bounded to the classes in a 1:1 manner would be a hint for this...
Mar 29 2019