Jump to page: 1 2
Thread overview
Any way to override base type with dervived in derived type
May 24, 2018
Alex
May 25, 2018
Basile B.
May 25, 2018
Basile B.
May 26, 2018
crimaniak
May 24, 2018
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)


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?










May 24, 2018
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?

https://en.wikipedia.org/wiki/Composition_over_inheritance
http://wiki.c2.com/?CompositionInsteadOfInheritance

Well, can imagine useful cases though...

>
> 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));
}
´´´
May 25, 2018
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.


May 25, 2018
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...
May 25, 2018
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; }
}








May 25, 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.
May 25, 2018
On Friday, 25 May 2018 at 01:42:48 UTC, Basile B. wrote:
> 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.

Who said I "think" they are know?

You missed the point where I said the code is designed to work this way. Forcing one to make their code more terse for some arbitrary sake of "safety" is also not a good way.

If there is only one point of assignment to T and that point assigned it a type TT : T then that is pretty damn safe. Sure, not 100% because anyone can do anything any time in a program. Nothing is 100% safe. It is not difficult to circumvent the type system that claims to be 100% correct. I am trying to work with the type system and inform it of a well defined behavior but it own't let me because it is too dense to understand.


May 25, 2018
On 5/24/18 4:24 PM, IntegratedDimensions wrote:

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

It should work, you are allowed covariant overloads. However, private functions are not virtual, you need to make them at least protected.

-Steve
May 25, 2018
On Friday, 25 May 2018 at 10:45:23 UTC, Steven Schveighoffer wrote:
> On 5/24/18 4:24 PM, IntegratedDimensions wrote:
>
>> 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
>>     ...
>> }
>
> It should work, you are allowed covariant overloads. However, private functions are not virtual, you need to make them at least protected.
>
> -Steve

Wow, I didn't expect that! Thanks, exactly what I needed.

Unfortunately property setters seem to be broken.

https://dpaste.dzfl.pl/24ce8c7e0681

Seems this is a bug though that has been fix as it only happens on certain the older compilers. I had to downgrade because in the latest I get a lot of link errors ;/


May 25, 2018
On Friday, 25 May 2018 at 10:45:23 UTC, Steven Schveighoffer wrote:
> On 5/24/18 4:24 PM, IntegratedDimensions wrote:
>
>> 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
>>     ...
>> }
>
> It should work, you are allowed covariant overloads. However, private functions are not virtual, you need to make them at least protected.
>
> -Steve

So, I upgraded everything, tried to add the setter and get an compile time access violation:


override @property T t(T v) { _t = v; return v; }

Changing T v to TT v gives the violation

override @property T t(TT v) { _t = v; return v; }


object.Error@(0): Access Violation
----------------
0x004850C8
0x00485C96
0x0043E22A
0x0047FB50
0x0046109A
0x0052401A
0x77D0B605 in LdrQueryProcessModuleInformation
0x77D11D02 in RtlQueryProcessLockInformation
0x77D11705 in RtlQueryProcessDebugInformation
0x77CA47DF in RtlAllocateHeap

While my specific use case is far more complex in design, the original essentially boils down to the dpaste I gave. Probably won't spent any time on it since I do not need the setter in this case(can use the field directly). But a bug is probably related to the code that fixed the "original bug" and possibly windows. What I do know is that 2.75 did not produce this access violation and it was the same code(I just uncommented out the setter).

The original dpaste code that did pass, I though, doesn't seem to pass now. I thought it was working for 2.80 and so I changed it to 2.69 and it failed but I might have made a mistake or the page lagged.

Either way, maybe you have a clue for future reference.

« First   ‹ Prev
1 2