August 04, 2021

On Tuesday, 3 August 2021 at 15:52:07 UTC, NonNull wrote:

>

Here, further down, I just saw Walter indicate his opinion that Any thoughts?

It gives limited value while having insane edge cases, hence a mistake.

August 04, 2021

On Tuesday, 3 August 2021 at 20:19:56 UTC, Alexandru Ermicioi wrote:

>

On Tuesday, 3 August 2021 at 17:18:48 UTC, bachmeier wrote:

>

On Tuesday, 3 August 2021 at 15:52:07 UTC, NonNull wrote:

>

Here, further down, I just saw Walter indicate his opinion that alias this is a mistake. Any thoughts?

https://news.ycombinator.com/item?id=28029184

The only argument I've heard against alias this is that it can be misused. That's kind of a joke argument in a language that has pointers.

Fyi, 'alias this' nicely fits for doing auto unboxing of a wrapper type into wrapped type, similar to how java does unboxing of primitives, not sure that it will be possible with mixin templates.

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.

August 04, 2021

On Wednesday, 4 August 2021 at 14:32:17 UTC, bachmeier wrote:

>

[snip]

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.

struct Foo {
    int x;
    alias x this;
}
August 04, 2021
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

-- 
It is not the employer who pays the wages. Employers only handle the money. It is the customer who pays the wages. -- Henry Ford
August 05, 2021

On Tuesday, 3 August 2021 at 15:52:07 UTC, NonNull wrote:

>

Here, further down, I just saw Walter indicate his opinion that alias this is a mistake. Any thoughts?

https://news.ycombinator.com/item?id=28029184

This is just surprising to me. I think alias this is a pretty cool thing... I use it within a mixin template (among stuff other stuff) in order to roll my own implementation of inheritance for entities in my game engine. Might be a bit goofy, but it does work pretty well for my hobbyist purposes, at least. How else are we going to get any kind of smooth "subtyping" with structs?

For what it's worth, the Jai language is lifting the idea wholesale with its using syntax. I think it has support for more than one of them as well.

If it's not a good idea today, how can we make it one?

August 05, 2021
On Wednesday, 4 August 2021 at 16:45:27 UTC, H. S. Teoh wrote:
> [snip]
>
> 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

Goods points throughout. Thanks for your perspective.

You describe an issue with upcasting. Would something like this only work in the context of classes and reference types? In other words, suppose alias this was removed from the language, but some more formal inheritance scheme was introduced for structs. Would it be able to have that behavior?
August 05, 2021

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 think that mixin templates beats alias this. You can only have one alias this which I think is a big disadvantage but you can have several mixin templates. You can also decide the visibility (or scope) for mixin templates. You want your mixin template to be visible outwards you just write.

mixin MyTemplate;

If you want to just use your mixin template inside your class/struct internally you can do that.

mixin MyTemplate instance;

which also enables several instances of the same mixin template.

mixin MyTemplate instance1;
mixin MyTemplate instance2;

Few D programmers know about this and it is badly documented. Also what happens with constructors/deconstructors doesn't seem to be documented at all. I'm disappointed with the documentation as mixin template is one of the central features of D (or should be). If we can point out to people in the documentation that alias this will be deprecated and link to mixin templates. Having a good tutorial/documentation about mixin templates and I think the discussions over alias this will be gone and people will adopt mixin templates instead.

August 05, 2021
On Thu, Aug 05, 2021 at 03:49:25PM +0000, jmh530 via Digitalmars-d wrote:
> On Wednesday, 4 August 2021 at 16:45:27 UTC, H. S. Teoh wrote:
> > [snip]
> > 
> > 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.
[...]
> Goods points throughout. Thanks for your perspective.
> 
> You describe an issue with upcasting. Would something like this only work in the context of classes and reference types? In other words, suppose alias this was removed from the language, but some more formal inheritance scheme was introduced for structs. Would it be able to have that behavior?

In C++, structs also support OO-style inheritance, and upcasting / downcasting works the same way as in classes.  So I suppose it could be made to work.

Note, however, that the reason D structs don't support inheritance, AIUI, is because of issues with this feature in C++. One of which is, the size of a variable of that type. Since a derived class implicitly converts to a base class, the same should apply to structs; but what happens when you try to pass a derived struct to a function that takes a base struct?  There would not be room to store the derived struct's data, which means you can only pass the base portion of the struct to the function.  Which in turn means overridden methods may try to access data that isn't there. IOW, this violates the Liskov Substitution Principle.

In C++, this is worked around by requiring passing by reference instead by value. But in D, this kinda defeats the purpose of structs, the whole point of which is a by-value type that gets passed around as "glorified ints", as Andrei calls it.  Once you need to pass things around by reference, structs lose their raison d'etre, and you might as well just use a class instead.


T

-- 
Just because you can, doesn't mean you should.
August 05, 2021
On Thursday, 5 August 2021 at 16:46:53 UTC, H. S. Teoh wrote:
>
> In C++, this is worked around by requiring passing by reference instead by value. But in D, this kinda defeats the purpose of structs, the whole point of which is a by-value type that gets passed around as "glorified ints", as Andrei calls it.  Once you need to pass things around by reference, structs lose their raison d'etre, and you might as well just use a class instead.
>

I never understood the benefit of structs are value types. In what use case would you like to pass the value of a struct rather than a reference. The struct will be copied on the stack which is inefficient so in 99% of the cases you would like to use the reference, especially when the parameter is const. If you need a copy the called function could be responsible for that.

Could someone explain the benefit of this "glorified int".


August 05, 2021
On Thu, Aug 05, 2021 at 05:24:56PM +0000, IGotD- via Digitalmars-d wrote: [...]
> I never understood the benefit of structs are value types. In what use case would you like to pass the value of a struct rather than a reference. The struct will be copied on the stack which is inefficient so in 99% of the cases you would like to use the reference, especially when the parameter is const. If you need a copy the called function could be responsible for that.
> 
> Could someone explain the benefit of this "glorified int".

I think your assumption that passing on the stack is inefficient is not necessarily correct.

Since the stack is almost constantly being accessed in the course of a program's execution, it remains in cache and so is much more cache-friendly than dereferencing a pointer to the heap.  Furthermore, if your struct is small enough, it can be passed entirely in CPU registers, which is much faster than any heap-allocated object can ever hope to be.  (And if your struct is too large, esp. if it's so large the compiler has to resort to passing a reference under the hood, you probably should be using a class in the first place.)

Also, a stack-allocated object has a well-defined lifetime that's trivially known to the optimizer, so in many cases good optimizing compilers like LDC can entirely elide the stack allocation and keep the struct in registers for its entire lifetime.  It's much harder for the optimizer to figure out the lifetime of a heap-allocated object, so it would conservatively skip such optimizations.

If performance is important to you, one of the first things you should be looking at is reducing heap usage where possible -- memory management is expensive, whether you use GC or some other method of memory management. Stack allocation is the best because deallocation comes "for free" when the function returns. Using structs instead of classes is a powerful tool for this purpose.


T

-- 
Маленькие детки - маленькие бедки.