D interface bug?

Alex sascha.orlov at gmail.com
Sat Mar 30 02:12:43 UTC 2019


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...


More information about the Digitalmars-d-learn mailing list