February 23, 2012
On Wednesday, February 22, 2012 16:25:09 Ali Çehreli wrote:
> On 02/22/2012 03:59 PM, Jonathan M Davis wrote:
> > On Wednesday, February 22, 2012 15:40:41 Ali Çehreli wrote:
> >> On 02/22/2012 03:16 PM, Blake Anderton wrote:
> >>> With an array the above tests work (I assume because each
> >>> operation is implicitly a new slice?).
> >> 
> >> It works because the slice itself is copied to count() and count()
> >> consumes that copy internally.
> >> 
> >> Now I see the issue here: Why does 'alias this' disable the parameter copying behavior? Why is the member slice not being copied?
> >> 
> >> This must be clarified. Is this a bug? A hole in the spec?
> > 
> > I believe that it's quite clear. Think about it. How is the template instantiated? With the type that you give it. And as long as the type
> 
> passes
> 
> > the template constraint, the function will be instantiated with the exact type that you gave it.
> 
> This proves that 'alias this' is something to watch out for. It makes our type to pass certain constraints but at the end it is still our object that gets passed, not the 'alias this'ed member.
> 
> The problem here has been that the original type was a class (i.e. a
> reference type). The class variable gets copied to count() and count()
> consumes the one instance of the class.
> 
> If the original code used 'struct' instead, this issue would not have come up, because then the member of the copy would be consumed. Sneaky! :)

Yes, but then you get all kinds of fun bugs due to the fact that your container is a pseudo value type. All of the instances share data as long as the array isn't caused to reallocate for some reason. So, you could end up with multiple copies affecting each other or having drastically different data, depending on what the code does.

But there are a variety of problems here.

Using classes for ranges makes it so that they do not work in the typically expected way. save is in ranges specifically for handling it, but it means that you have to actually _use_ it, which many algorithms should but probably don't, because they're only tested with ranges which are structs or arrays.

Conflating ranges and containers is a bad idea which will cause issues. They are distinct concepts which should be kept separate.

alias this makes it so that that type acts like the aliased type in a number of circumstances, but it still isn't _exactly_ that type, so it won't act exactly the same as the aliased type in all circumstances (templates being a prime example). But if you think about it, since it _is_ a different type, it _can't_ act like the aliased type in all circumstances. You just have to be well aware of what the exact effects are. But templated functions (and range- based functions in particular) are likely to cause issues, because they'll compile thanks to the alias, but they won't be instantiated with the aliased type.

- Jonathan M Davis
February 23, 2012
Good points on why an explicit range distinction is desired; I probably should have looked harder at how std.container worked. I'm also probably spoiled/unlearning from C#'s IEnumerable<T> syntactic sugar (foreach over enumerator, LINQ) which handles most of these considerations for you. You could say it encourages conflating the container with the iteration mechanism (range in D, enumerator in C#). I see the distinction now but old habits die hard, I guess. :)
1 2
Next ›   Last »