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