January 11, 2013
On Friday, 11 January 2013 at 13:27:46 UTC, deadalnix wrote:
> On Friday, 11 January 2013 at 11:14:27 UTC, monarch_dodra wrote:
>> In particular, if you define an opIndexAssign, then the RHS *has* to be evaluated first (from a "visual" point of view, since LHS is pretty much "this").
>>
>> //----
>> a[0] = a[0]
>> //----
>> Becomes
>> //----
>> a.opIndexAssign(a[0], 0);
>> //----
>>
>
> It is probably not gonna change at this point, but opIndexAssign seems wrong here, not the LTR evaluation.

I find that with LTR, you end up with a value (T.init), that you never actually put into your array. IMO, that means something went wrong somewhere. This is particularly relevant with "++a[0]".

I was wondering: if AA's are "library types", as we have been claiming they are, then *how* does
//----
a[0] = a[0];
//----

Even work? The way I see it, there is 1 of 2 possible implementations:

==== 1 ====
Simple "ref T opIndex(size_t);" implementation: This does not allow distinguishing read from write, so would crash on *any* call to an empty field.

==== 2 ====
With opIndexAssign. But if we *did* have opIndexAssign, then we wouldn't have these problems, and the code such as "++a[0];" would correctly throw.

So what gives? My guess is that the compiler only has opIndex, but cheats to know if it is a write:
//----
immutable(int)[int] a;
a[0] = 5;
//----
Error: a[0] isn't mutable
//----

That's a tell-tale sign the compiler is cheating on us. What's more, if AA used opIndexAssign, this assignment would actually work.
January 11, 2013
While we chat, the good Hara delivers :o)

https://github.com/D-Programming-Language/dmd/pull/1465

Bye,
bearophile
January 11, 2013
On Fri, Jan 11, 2013 at 08:53:44AM +0100, Don wrote:
> Consider this code:
> ---
>    int[int] x;
> 
>    int k = x[2] + 5; // Error, range violation. Makes sense.
> 
>    x[2] = x[2] + 5;  // But this works!!!
> ---
> 
> That is, x[2] doesn't exist, *unless you are about to assign to it*.

http://d.puremagic.com/issues/show_bug.cgi?id=3825


> What happens is:
> 1. lvalue index (creates x[2], sets it to int.init)

Actually it doesn't. It binary-zeroes the entry.


> 2. rvalue index (returns x[2], which is now 0)
> 3. lvalue index assign (sets x[2] = 5)
> 
> In reality, step 1 returns a pointer to the newly created element.

Yeah that's pretty much what's happening currently.


> How could this be implemented as a library type?

I'd argue that this behaviour is a bug, and *shouldn't* be implemented in the library type. This behaviour causes, for example, real[string] to have 0.0 as default entry value instead of nan, like the rest of the language.


> The superficially similar case, x[2] += 5; can be implemented with opIndexOpAssign. But I don't know how to do this one.

There's another problem: there is currently no operator overload that can handle things like a['b']['c']=d, because the first [] is a lookup and the second [] is an assignment.


> Note that elements are not always magically created when an lvalue is required. AFAIK it only happens in assignment. For example this code gives a runtime error:
> ---
> void foo(ref int g) { ++g; }
> 
>    int[int] x;
>    foo( x[2] );  // range error, x[2] doesn't exist yet
> ---

I think this is correct behaviour. The previous case I consider a bug.


T

-- 
No! I'm not in denial!
January 12, 2013
On 01/11/2013 08:53 AM, Don wrote:
> Consider this code:
> ---
>     int[int] x;
>
>     int k = x[2] + 5; // Error, range violation. Makes sense.
>
>     x[2] = x[2] + 5;  // But this works!!!
> ---
>
> That is, x[2] doesn't exist, *unless you are about to assign to
> it*.
>
> What happens is:
> 1. lvalue index (creates x[2], sets it to int.init)
> 2. rvalue index (returns x[2], which is now 0)
> 3. lvalue index assign (sets x[2] = 5)
>
> In reality, step 1 returns a pointer to the newly created element.
>
> How could this be implemented as a library type?
> The superficially similar case, x[2] += 5; can be implemented
> with opIndexOpAssign. But I don't know how to do this one.
>

struct S{
	private int[int] x;
	int opIndex(int k){
		return x[k];
	}
	void opIndexAssign(lazy int v, int k){
		x[k]=v;
	}
}

void main(){
	S x;
	int k = x[2] + 5; // Error
	x[2] = x[2] + 5;  // Ok
}

=P

> Note that elements are not always magically created when an
> lvalue is required. AFAIK it only happens in assignment. For
> example this code gives a runtime error:
> ---
> void foo(ref int g) { ++g; }
>
>     int[int] x;
>     foo( x[2] );  // range error, x[2] doesn't exist yet
> ---

Now I'm lost too. Anyway, I do not consider the behaviour particularly useful.


January 12, 2013
On Friday, 11 January 2013 at 13:50:33 UTC, monarch_dodra wrote:
> I find that with LTR, you end up with a value (T.init), that you never actually put into your array. IMO, that means something went wrong somewhere. This is particularly relevant with "++a[0]".
>

Not necessarily, as the computation made for an assignation isn't the one made in order to get the value.

It make sense that such expression fail. As long as the AA as the key are computed before the value and in that order.
January 12, 2013
On Saturday, 12 January 2013 at 04:24:01 UTC, deadalnix wrote:
> On Friday, 11 January 2013 at 13:50:33 UTC, monarch_dodra wrote:
>> I find that with LTR, you end up with a value (T.init), that you never actually put into your array. IMO, that means something went wrong somewhere. This is particularly relevant with "++a[0]".
>>
>
> Not necessarily, as the computation made for an assignation isn't the one made in order to get the value.
>
> It make sense that such expression fail. As long as the AA as the key are computed before the value and in that order.

I guess I can see it either way. My only gripe though is that:
"a[0] = a[0] + 5;"

Could never work if AA's were a "true" library type. I think this is a problem, if the plan is to one day completely move AA's out of the compiler.

That, and for generic programming, it means my user written HashMap! will never be able to have AA's semantics.
January 12, 2013
On Saturday, January 12, 2013 11:51:59 monarch_dodra wrote:
> On Saturday, 12 January 2013 at 04:24:01 UTC, deadalnix wrote:
> > On Friday, 11 January 2013 at 13:50:33 UTC, monarch_dodra wrote:
> >> I find that with LTR, you end up with a value (T.init), that you never actually put into your array. IMO, that means something went wrong somewhere. This is particularly relevant with "++a[0]".
> > 
> > Not necessarily, as the computation made for an assignation isn't the one made in order to get the value.
> > 
> > It make sense that such expression fail. As long as the AA as the key are computed before the value and in that order.
> 
> I guess I can see it either way. My only gripe though is that: "a[0] = a[0] + 5;"
> 
> Could never work if AA's were a "true" library type. I think this is a problem, if the plan is to one day completely move AA's out of the compiler.
> 
> That, and for generic programming, it means my user written HashMap! will never be able to have AA's semantics.

But that should result in a RangeError. It's not something that's supposed to work. It's a bug. So, the fact that a library type couldn't duplicate it is irrelevant. The fact that it can't have a particular bug isn't exactly a problem.

- Jonathan M Davis
January 12, 2013
On Saturday, 12 January 2013 at 12:05:30 UTC, Jonathan M Davis wrote:
> On Saturday, January 12, 2013 11:51:59 monarch_dodra wrote:
>> 
>> I guess I can see it either way. My only gripe though is that:
>> "a[0] = a[0] + 5;"
>> 
>> Could never work if AA's were a "true" library type. I think this
>> is a problem, if the plan is to one day completely move AA's out
>> of the compiler.
>> 
>> That, and for generic programming, it means my user written
>> HashMap! will never be able to have AA's semantics.
>
> But that should result in a RangeError. It's not something that's supposed to
> work. It's a bug. So, the fact that a library type couldn't duplicate it is
> irrelevant. The fact that it can't have a particular bug isn't exactly a
> problem.
>
> - Jonathan M Davis

Oh... Right...

I guess I missread this thread (and 9rnsr's pull), and was lead to understand that we were going the road of accepting this.

Well, my bad than.

What about "++a[0]" when there is no a[0]? Is this something that will throw or not?

As well, what about
//----
immutable(int)[int] aa;
aa[0] = 5;
//----
This should work, right?
January 12, 2013
On Saturday, January 12, 2013 13:30:47 monarch_dodra wrote:
> On Saturday, 12 January 2013 at 12:05:30 UTC, Jonathan M Davis
> 
> wrote:
> > On Saturday, January 12, 2013 11:51:59 monarch_dodra wrote:
> >> I guess I can see it either way. My only gripe though is that: "a[0] = a[0] + 5;"
> >> 
> >> Could never work if AA's were a "true" library type. I think
> >> this
> >> is a problem, if the plan is to one day completely move AA's
> >> out
> >> of the compiler.
> >> 
> >> That, and for generic programming, it means my user written HashMap! will never be able to have AA's semantics.
> > 
> > But that should result in a RangeError. It's not something
> > that's supposed to
> > work. It's a bug. So, the fact that a library type couldn't
> > duplicate it is
> > irrelevant. The fact that it can't have a particular bug isn't
> > exactly a
> > problem.
> > 
> > - Jonathan M Davis
> 
> Oh... Right...
> 
> I guess I missread this thread (and 9rnsr's pull), and was lead to understand that we were going the road of accepting this.
> 
> Well, my bad than.

I don't know what he's doing in his pull, but I think that it's clear that it should be a RangeError, and I don't think that much of anyone in this thread is disputing that.

> What about "++a[0]" when there is no a[0]? Is this something that will throw or not?

Personally, I think that it should, but there's probably a good chance that it won't, because it's an lvalue. I don't know what will happen with that though. There are a lot of bugs right now related elements being inserted into AAs when they shouldn't be, so I really don't know how much relation the current behavior will have with the behavior that AAs will ultimately have.

> As well, what about
> //----
> immutable(int)[int] aa;
> aa[0] = 5;
> //----
> This should work, right?

That should definitely work. That's how you add elements to AA. My gripe with something like ++a[0] working when there's no a[0] is that you'd be adding to an element that doesn't exist yet.

- Jonathan M Davis
January 12, 2013
On Saturday, 12 January 2013 at 12:41:12 UTC, Jonathan M Davis wrote:
>
> My gripe with
> something like ++a[0] working when there's no a[0] is that you'd be adding to
> an element that doesn't exist yet.
>
> - Jonathan M Davis

Not to mention, if "++" throws, I bet you'd insert T.init in a[0]. I don't see it behaving any other way.