Any way to override base type with dervived in derived type

Basile B. b2.temp at gmx.com
Fri May 25 01:42:48 UTC 2018


On Friday, 25 May 2018 at 01:17:45 UTC, IntegratedDimensions 
wrote:
> On Friday, 25 May 2018 at 01:02:00 UTC, Basile B. wrote:
>> On Friday, 25 May 2018 at 00:15:39 UTC, IntegratedDimensions 
>> wrote:
>>> On Thursday, 24 May 2018 at 23:31:50 UTC, Alex wrote:
>>>> On Thursday, 24 May 2018 at 20:24:32 UTC, 
>>>> IntegratedDimensions wrote:
>>>>> class T;
>>>>> class TT : T;
>>>>>
>>>>> interface I
>>>>> {
>>>>>    @property T t();
>>>>> }
>>>>>
>>>>> abstract class A
>>>>> {
>>>>>    T _t;
>>>>>    @property T t() { return _t; }
>>>>>
>>>>> }
>>>>>
>>>>> class C : A
>>>>> {
>>>>>
>>>>>    // Stuff below uses t as TT but compiler, of course, 
>>>>> treats t as T
>>>>>    ...
>>>>> }
>>>>>
>>>>>
>>>>> The issue is that I programmed the class C with a variable 
>>>>> that directly was based off TT, I later subderived T from 
>>>>> TT and exposed it in I. (TT was refactored in to T and not 
>>>>> T)
>>>>>
>>>>
>>>> As as a side note:
>>>> I can hardly follow this, as you don't show, where you use 
>>>> the interface I. However, especially if TT was refactored in 
>>>> such a way, that is a set difference of T and not T, why you 
>>>> choose to derive from T instead of to contain T?
>>>>
>>>
>>> It really should be obvious that A was meant to derive from 
>>> I. This is just standard oop.  Simply leaving off : I should 
>>> not be a deal breaker because it would not change the whole 
>>> problem from black to white or vice versa.
>>>
>>> T is a member to be included. You can only derive from one 
>>> class. C can't derive from both A and T and even if it did, 
>>> it would mean something else.
>>>
>>>
>>>
>>>> https://en.wikipedia.org/wiki/Composition_over_inheritance
>>>> http://wiki.c2.com/?CompositionInsteadOfInheritance
>>>>
>>>> Well, can imagine useful cases though...
>>>>
>>>
>>> This is not a composition pattern.
>>>
>>> This is a parallel inherentence pattern.
>>>
>>> TT : T = T
>>> |    |   |
>>> v    v   v
>>> C  : A : I
>>>
>>> TT is used with C and T with I.
>>>
>>> When C changes to C', TT : T changes to TT' : T
>>>
>>> All functions that use TT in C are forced to use it as if it 
>>> were of type T rather than TT which requires a bunch of casts.
>>>
>>> This is generally a violation of type logic. There is nothing 
>>> in that prevents t from being something like TTT which has no 
>>> direct relation to TT.
>>>
>>> But the programming logic of the code enforces t to be of 
>>> type TT in C *always*. So I don't know why I would have to 
>>> use casting all the time. It would be nice if there where a 
>>> simple logical way to enforce a design pattern in the type 
>>> system knowing that it is enforced at runtime. This makes 
>>> cleaner code, nothing else.
>>>
>>>
>>>
>>>>>
>>>>> But all the code in C assumes t is of type TT but now due 
>>>>> to the interface it looks like a T, even though internally 
>>>>> it is actually a TT.
>>>>>
>>>>> What I'd like to do is
>>>>>
>>>>> class C : A
>>>>> {
>>>>>    private override @property TT t() { return cast(TT)(_t); 
>>>>> } // null check if necessary
>>>>>    // Stuff below uses t which is now a TT
>>>>>    ...
>>>>> }
>>>>>
>>>>> or whatever.
>>>>>
>>>>> This is simply so I don't have to rename or cast all my 
>>>>> uses of t in C to type TT.
>>>>>
>>>>> I'm pretty much guaranteed that in C, t will be type TT due 
>>>>> to the design(C goes with TT like bread with butter).
>>>>>
>>>>> So, it would be nice if somehow I could inform the type 
>>>>> system that in C, t is always of type TT and so treat it as 
>>>>> such rather than forcing me to explicitly cast for every 
>>>>> use. Again, I could rename things to avoid the same name 
>>>>> usage but in this case it is not necessary because of the 
>>>>> design.
>>>>>
>>>>> Is there any semantics that can get me around having to 
>>>>> rename?
>>>>
>>>> Maybe, you are looking for Curiously Recurring Template 
>>>> Pattern?
>>>>
>>>> ´´´
>>>> interface I(P)
>>>> {
>>>> 	@property P t();
>>>> }
>>>>
>>>> abstract class T(P) : I!P
>>>> {
>>>>     P _p;
>>>>     @property P t() { return _p; }
>>>> }
>>>>
>>>> class TT : T!TT
>>>> {
>>>>
>>>> }
>>>>
>>>> void main()
>>>> {
>>>> 	auto tt = new TT();
>>>> 	static assert(is(typeof(tt.t) == TT));
>>>> }
>>>> ´´´
>>>
>>> No, I am trying to keep parallel derived types consistently 
>>> connected. If A is derived from B and C from D and B uses D 
>>> then A uses C. Consistency cannot be guaranteed by the type 
>>> system at compile time because A is typed to use C, I want to 
>>> restrict it further to D.
>>
>> You must put a template parameter in the interface and 
>> specialize the class that implements the interface.
>>
>> ```
>> module runnable;
>>
>> class T{}
>> class TT : T{}
>>
>> interface I(N)
>> {
>>    @property N t();
>> }
>>
>> abstract class A(N) : I!N
>> {
>>    N _t;
>>    @property N t() { return _t; }
>> }
>>
>> class C1 : A!T{}
>> class C2 : A!TT{}
>>
>> void main(string[] args)
>> {
>>     import std.traits;
>>     static assert(is(ReturnType!(C1.t) == T));
>>     static assert(is(ReturnType!(C2.t) == TT));
>> }
>>
>> module runnable;
>>
>> class T{}
>> class TT : T{}
>>
>> interface I(N)
>> {
>>    @property N t();
>> }
>>
>> abstract class A(N) : I!N
>> {
>>    N _t;
>>    @property N t() { return _t; }
>> }
>>
>> class C1 : A!T{}
>> class C2 : A!TT{}
>>
>> void main(string[] args)
>> {
>>     import std.traits;
>>     static assert(is(ReturnType!(C1.t) == T));
>>     static assert(is(ReturnType!(C2.t) == TT));
>> }
>> ```
>>
>> but obviously this won't work if you want to derive C1 or C2...
>
>
> or if there 100 fields.
>
> This isn't a proper solution.
>
> The whole issue is not outside of C but inside
>
> Hypothetically
>
> class C : A
> {
>    @property TT : T t() { return _t; }
>
>    // t can be used directly as TT rather than having to do 
> (cast(TT)t) everywhere t is used.
> }
>
> would solve the problem and it would scale.
>
> The way it would work is that inside C, t is treated as type TT 
> which is derived from T. The derivation means that it satisfies 
> the interface constraint since we can always stick a TT in a T.
>
> Since the type system doesn't allow such behavior, I'm trying 
> to find a convenient way to simulate it that isn't more 
> complicated than just casting. The problem is that casting is 
> very verbose and scales with the code size.
>
> Also, such notation would still work with deriving from C
>
> class CC : C
> {
>    @property TTT : TT t() { return _t; }
> }

Using dynamic castc while you think that the static type should 
be known is not good anyway.


More information about the Digitalmars-d-learn mailing list