Is `alias this` a mistake?

12345swordy alexanderheistermann at gmail.com
Sat Aug 7 16:10:10 UTC 2021


On Wednesday, 4 August 2021 at 16:45:27 UTC, H. S. Teoh wrote:
> On Wed, Aug 04, 2021 at 03:13:07PM +0000, jmh530 via 
> Digitalmars-d wrote:
>> On Wednesday, 4 August 2021 at 14:32:17 UTC, bachmeier wrote:
> [...]
>> > It's difficult to beat the simplicity of alias this. Even if 
>> > something cam be done with mixin templates, that's a lot of 
>> > overhead vs `alias x this;`. I can't say I use mixin 
>> > templates very often, but am skeptical that there's no way 
>> > to misuse them.
>> 
>> I'm sympathetic to this.
>> 
>> I think if you got rid of alias this, then you would end up 
>> needing something else to accomplish some form of subtyping 
>> relationship for structs beyond just composition. For 
>> instance, the struct below can be used anywhere an int can be 
>> used. Without alias this, you either need to pull out that x 
>> value each time it is used in a function or you need to write 
>> overloads for all the functions you want to use Foo with like 
>> an int. Alternately, you can modify those functions to become 
>> generic and handle things that are like an int, but then what 
>> if you don't control those functions? It's just so simple.
>> 
>> ```d
>> struct Foo {
>>     int x;
>>     alias x this;
>> }
>> ```
>
> This is exactly why I used to love alias this. It lets me 
> cowboy a wrapper type into code that wasn't originally intended 
> to handle it, and it works wonderfully with minimal code 
> changes.  I used to do this all the time in my code, and it let 
> me get away with quick fixes that for the most part worked well.
>
> Unfortunately, the long term effect of this sort of hackish 
> solution is a gradual degradation of maintainability and code 
> readability. After a while, when new code needs to be added, 
> it's not clear whether I should use type X, or wrapper type Y 
> with alias this X, or wrapper type Z with alias this Y (chained 
> `alias this` used to be my favorite trick).  It became 
> context-dependent -- if I needed special functionality provided 
> by wrapper type Y, then I'd use Y; if Z provided something I 
> needed at the time then I'd use Z.  But soon I find out that I 
> need *both* Z and Y in the same function, so my code started to 
> get cluttered with interconversions between these wrappers.  
> Soon I had to invent yet another wrapper type that encompasses 
> *both* Z and Y just to get all the data I need in one place. 
> Which in turn leads to further such issues later down the road.
>
> Worse yet, (no) thanks to `alias this`'s super-convenient 
> implicit conversions, half of the code looks like it takes Y 
> but actually receives only X -- it's not immediately obvious 
> because Y implicitly converts to X. So when I need to propagate 
> Y's functionality down into code expecting only X, I find 
> myself in a bind. And unlike actual OO polymorphism there is no 
> equivalent in `alias this` to an upcast: once Y decays to X you 
> cannot get Y back.  Which means that now, the code that 
> originally received only X had to be revised to receive Y.  
> Also, code that receives X have no obvious connection to Y: 
> there is no class hierarchy that you can look up to learn 
> possible relationships between class X and class Y -- X can be 
> the target of an implicit `alias this` conversion of literally 
> *any* type anywhere in the program. It's highly unstructured 
> subtyping that hurts long-term code readability and 
> maintainability.
>
> Taking a step back from this cascading complexity, I realized 
> that had I *not* used alias this, I would have been forced to 
> consider how to encapsulate the functionality of X, Y *and* Z 
> in a single common type (or a proper class hierarchy) instead.  
> It would have been much less convenient, but in the long run it 
> would have improved code quality: instead of the above 
> spaghetti involving shuffling the same danged data between X, 
> Y, and Z, trying to figure out which type to accept and where 
> implicit conversions are happening, there would have been a 
> single unified type that everyone accepts.  There would be no 
> question which (wrapper) type to use because there would be no 
> wrapper types.  And there would be no spurious implicit 
> conversions to trip you up when reading the code.  The code 
> would be cleaner and more consistent -- instead of various 
> modules accepting different variations on X, Y, and Z, there 
> would be a single type that serves as common currency between 
> all code, eliminating a lot of needless complexity and implicit 
> conversion messiness.
>
> This is why, even though I loved alias this (and still do to 
> some extent), I have come to realize that in the long term, it 
> lies more on the negative side of the scale than the positive.
>
>
> T

Speaking of weird edge case. This code compiles.
struct TIntStatic
{

static int mX;

static @property int x() { return mX; }
static @property void x(int v) { mX = v; }

alias x this;
}
alias t = TIntStatic;
t = 5; // According to Walter bright this shouldn't compile, yet 
it does.


More information about the Digitalmars-d mailing list