digitalmars.D - More magical AA semantics
- Don (24/24) Jan 10 2013 Consider this code:
- Jonathan M Davis (11/43) Jan 11 2013 I would argue that the fact that
- Bernard Helyer (4/41) Jan 11 2013 I completely agree. Doesn't the spec say that relying on
- Mehrdad (2/2) Jan 11 2013 Shouldn't the rhs be evaluated before the lhs? Why would it be
- deadalnix (3/5) Jan 11 2013 After a long discussion with Andrei, it seems that it is left to
- Jens Mueller (13/19) Jan 11 2013 Then the spec should be fixed.
- deadalnix (4/27) Jan 11 2013 It shouldn't pass as the entry has never been assigned when
- Jens Mueller (7/39) Jan 11 2013 Really? I thought the semantics (of ++a) are increment a and return a
- monarch_dodra (9/37) Jan 11 2013 It is meant to be an lvalue, and is the reason this is legal in
- deadalnix (3/11) Jan 11 2013 OK, it seems I talked too fast here. Still it seems very weird to
- kenji hara (8/13) Jan 11 2013 aa[key] = val;
- monarch_dodra (23/29) Jan 11 2013 This seems wrong to me.
- deadalnix (3/13) Jan 11 2013 It is probably not gonna change at this point, but opIndexAssign
- monarch_dodra (30/45) Jan 11 2013 I find that with LTR, you end up with a value (T.init), that you
- bearophile (4/4) Jan 11 2013 While we chat, the good Hara delivers :o)
- deadalnix (5/9) Jan 11 2013 Not necessarily, as the computation made for an assignation isn't
- monarch_dodra (8/18) Jan 12 2013 I guess I can see it either way. My only gripe though is that:
- Jonathan M Davis (6/28) Jan 12 2013 But that should result in a RangeError. It's not something that's suppos...
- monarch_dodra (14/35) Jan 12 2013 Oh... Right...
- Jonathan M Davis (13/53) Jan 12 2013 I don't know what he's doing in his pull, but I think that it's clear th...
- monarch_dodra (4/9) Jan 12 2013 Not to mention, if "++" throws, I bet you'd insert T.init in
- Jonathan M Davis (5/17) Jan 12 2013 Which is precisely one of the AA bugs that I'm most annoyed with:
- monarch_dodra (4/22) Jan 12 2013 Well, it looks like this is fixed in 9rnsr's pull. I'll ask him
- Andrei Alexandrescu (4/21) Jan 12 2013 Shouldn't work.
- monarch_dodra (17/48) Jan 12 2013 OK. But then let's also try to solve these standing issues:
- monarch_dodra (7/12) Jan 12 2013 I can answer that actually: It *must* throw an exception, because
- deadalnix (3/20) Jan 12 2013 Can you explain why ?
- Andrei Alexandrescu (3/22) Jan 12 2013 Did so in https://github.com/D-Programming-Language/dmd/pull/1465
- Jonathan M Davis (8/20) Jan 12 2013 Yeah, you're right. I didn't think through that one enough. Why insertin...
- H. S. Teoh (23/43) Jan 12 2013 Yeah, like:
- Andrei Alexandrescu (3/22) Jan 12 2013 But a[0] += 5 can be made to work.
- Don (20/46) Jan 11 2013 That's my feeling too. I think that if we want to implement AAs
- Jonathan M Davis (14/17) Jan 11 2013 Well, the AAs are _already_ a library type. That's a large part of why t...
- Don (14/34) Jan 11 2013 Unfortunately, that's not true. All that happened was that a
- Jens Mueller (10/18) Jan 11 2013 I think the last statement is illegal. Because from
- bearophile (4/4) Jan 11 2013 See also:
- Daniel Murphy (3/9) Jan 11 2013 Definitely a bug.
- Jens Mueller (10/40) Jan 11 2013 I don't know how opIndex is defined and I cannot find appropriate
- H. S. Teoh (15/44) Jan 11 2013 Actually it doesn't. It binary-zeroes the entry.
- Timon Gehr (18/42) Jan 11 2013 struct S{
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. 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 ---
Jan 10 2013
On Friday, January 11, 2013 08:53:44 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. 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 would argue that the fact that x[2] = x[2] + 5; works is a bug. Given some of the weirdness that happens with assigning to AA elements, it doesn't entirely surprise me. For instance, x[2] = funcThatThrows(); results in x[2] holding the init value of x's element type. But I think that it's indicative of problems with the current AA implementation which need to be fixed and not something that we should be trying to emulate with library types. - Jonathan M Davis
Jan 11 2013
On Friday, 11 January 2013 at 08:26:54 UTC, Jonathan M Davis wrote:On Friday, January 11, 2013 08:53:44 Don wrote:I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?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. 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 would argue that the fact that x[2] = x[2] + 5; works is a bug.
Jan 11 2013
Shouldn't the rhs be evaluated before the lhs? Why would it be undefined/unspecified/etc.?
Jan 11 2013
On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?After a long discussion with Andrei, it seems that it is left to right.
Jan 11 2013
deadalnix wrote:On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:Then the spec should be fixed. unittest { int a = 0; ++a = a; assert(a == 1); } Don't know though whether you find it surprising that the above code passes? But whether it is left to right or right to left does not matter that much. At least it's defined and you can internalize it. The more I think about the more sense it makes to have it left to right. JensI completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?After a long discussion with Andrei, it seems that it is left to right.
Jan 11 2013
On Friday, 11 January 2013 at 10:16:28 UTC, Jens Mueller wrote:deadalnix wrote:++a isn't supposed to be an lvalue (it is not assignable).On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:Then the spec should be fixed. unittest { int a = 0; ++a = a; assert(a == 1); }I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?After a long discussion with Andrei, it seems that it is left to right.Don't know though whether you find it surprising that the above code passes? But whether it is left to right or right to left does not matter that much. At least it's defined and you can internalize it. The more I think about the more sense it makes to have it left to right.It shouldn't pass as the entry has never been assigned when computing the value.
Jan 11 2013
deadalnix wrote:On Friday, 11 January 2013 at 10:16:28 UTC, Jens Mueller wrote:Really? I thought the semantics (of ++a) are increment a and return a reference to it. Whereas a++ is rewritten to (auto t = e, ++e, t). That means it returns a copy of the old value of a.deadalnix wrote:++a isn't supposed to be an lvalue (it is not assignable).On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:Then the spec should be fixed. unittest { int a = 0; ++a = a; assert(a == 1); }I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?After a long discussion with Andrei, it seems that it is left to right.Isn't this a speciality of AAs in D/C++? When you access an non existing element it gets created? You do not need to assign to it. JensDon't know though whether you find it surprising that the above code passes? But whether it is left to right or right to left does not matter that much. At least it's defined and you can internalize it. The more I think about the more sense it makes to have it left to right.It shouldn't pass as the entry has never been assigned when computing the value.
Jan 11 2013
On Friday, 11 January 2013 at 10:43:33 UTC, Jens Mueller wrote:deadalnix wrote:It is meant to be an lvalue, and is the reason this is legal in both C++ and D: int main() { int a = 0; ++++a; return 0; }On Friday, 11 January 2013 at 10:16:28 UTC, Jens Mueller wrote:Really? I thought the semantics (of ++a) are increment a and return a reference to it. Whereas a++ is rewritten to (auto t = e, ++e, t). That means it returns a copy of the old value of a.deadalnix wrote:++a isn't supposed to be an lvalue (it is not assignable).On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:Then the spec should be fixed. unittest { int a = 0; ++a = a; assert(a == 1); }I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?After a long discussion with Andrei, it seems that it is left to right.
Jan 11 2013
On Friday, 11 January 2013 at 10:59:00 UTC, monarch_dodra wrote:It is meant to be an lvalue, and is the reason this is legal in both C++ and D: int main() { int a = 0; ++++a; return 0; }OK, it seems I talked too fast here. Still it seems very weird to me :D
Jan 11 2013
aa[key] = val; should be evaluated: 1. aa 2. key 3. val 4. aa[key] = val <-- allocating slot and set to it Kenji Hara 2013/01/11 18:56 "deadalnix" <deadalnix gmail.com>:On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?After a long discussion with Andrei, it seems that it is left to right.
Jan 11 2013
On Friday, 11 January 2013 at 09:50:18 UTC, deadalnix wrote:On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:This seems wrong to me. 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); //---- On a related note, I don't know how AA's are actually implemented, but it sounds like giving them opIndexXXX would solve a lot of our problems: * Initialization to T.init if RHS throws * a[0] = a[0]; runs if there is no a[0]. Or even code such as this: //---- int[int] a; ++a[0]; //---- In what universe do we actually expect this to work? It shouldn't.I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?After a long discussion with Andrei, it seems that it is left to right.
Jan 11 2013
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.
Jan 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: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.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.
Jan 11 2013
While we chat, the good Hara delivers :o) https://github.com/D-Programming-Language/dmd/pull/1465 Bye, bearophile
Jan 11 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.
Jan 11 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 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.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.
Jan 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: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 DavisOn Friday, 11 January 2013 at 13:50:33 UTC, 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.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.
Jan 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: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?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
Jan 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: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.On Saturday, January 12, 2013 11:51:59 monarch_dodra wrote: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 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 DavisWhat 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
Jan 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 DavisNot to mention, if "++" throws, I bet you'd insert T.init in a[0]. I don't see it behaving any other way.
Jan 12 2013
On Saturday, January 12, 2013 13:46:57 monarch_dodra wrote:On Saturday, 12 January 2013 at 12:41:12 UTC, Jonathan M Davis wrote:Which is precisely one of the AA bugs that I'm most annoyed with: aa[0] = funcThatThrows(); inserts T.init at aa[0]. - Jonathan M DavisMy 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 DavisNot to mention, if "++" throws, I bet you'd insert T.init in a[0]. I don't see it behaving any other way.
Jan 12 2013
On Saturday, 12 January 2013 at 12:52:43 UTC, Jonathan M Davis wrote:On Saturday, January 12, 2013 13:46:57 monarch_dodra wrote:Well, it looks like this is fixed in 9rnsr's pull. I'll ask him directly regarding "++".On Saturday, 12 January 2013 at 12:41:12 UTC, Jonathan M Davis wrote:Which is precisely one of the AA bugs that I'm most annoyed with: aa[0] = funcThatThrows(); inserts T.init at aa[0]. - Jonathan M DavisMy 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 DavisNot to mention, if "++" throws, I bet you'd insert T.init in a[0]. I don't see it behaving any other way.
Jan 12 2013
On 1/12/13 7:40 AM, Jonathan M Davis wrote:On Saturday, January 12, 2013 13:30:47 monarch_dodra wrote:++a[0] should work.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.Shouldn't work. AndreiAs 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.
Jan 12 2013
On Saturday, 12 January 2013 at 21:56:13 UTC, Andrei Alexandrescu wrote:On 1/12/13 7:40 AM, Jonathan M Davis wrote:OK. But then let's also try to solve these standing issues: Given a T[int], where T defines op++: 1. a[0] must be initialized to T.init first (and not memory zero'd, especially if T is user defined). 2. a must not be modified if T.op++ throws an exception. Q. Is this specific to *operators* that mutate? What about normal function?: a[0].mutate(); Q.1) create a T.init and call mutate on it? Q.2) Throw an exceptionOn Saturday, January 12, 2013 13:30:47 monarch_dodra wrote:++a[0] should work.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.I can see the problem: What if the key is already there? However, I think this is just a limitation of the syntax. If AA's had an "insert" function that only inserts when the key doesn't exist (akin to C++'s set::insert), then we'd be able to have our AA's of immutables.Shouldn't work. AndreiAs 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.
Jan 12 2013
On Saturday, 12 January 2013 at 22:44:19 UTC, monarch_dodra wrote:Q. Is this specific to *operators* that mutate? What about normal function?: a[0].mutate(); Q.1) create a T.init and call mutate on it? Q.2) Throw an exceptionI can answer that actually: It *must* throw an exception, because the implementation would have a hook for "opIndexFunction", so it would call "opIndex" + "Function", which would throw. I'm not thrilled at the difference of behavior, but I guess we can make an exception and support "lazy insertion" for numeric operations.
Jan 12 2013
On Saturday, 12 January 2013 at 21:56:13 UTC, Andrei Alexandrescu wrote:On 1/12/13 7:40 AM, Jonathan M Davis wrote:Can you explain why ?On Saturday, January 12, 2013 13:30:47 monarch_dodra wrote:++a[0] should work.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.
Jan 12 2013
On 1/12/13 8:26 PM, deadalnix wrote:On Saturday, 12 January 2013 at 21:56:13 UTC, Andrei Alexandrescu wrote:Did so in https://github.com/D-Programming-Language/dmd/pull/1465 AndreiOn 1/12/13 7:40 AM, Jonathan M Davis wrote:Can you explain why ?On Saturday, January 12, 2013 13:30:47 monarch_dodra wrote:++a[0] should work.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.
Jan 12 2013
On Saturday, January 12, 2013 16:56:13 Andrei Alexandrescu wrote:Yeah, you're right. I didn't think through that one enough. Why inserting immutable elements would work ideally, there's no difference between inserting and assigning as far as the operatiors used or functions called go, so we're forced to disallow insertion as well as assignment. So, assuming that the functions for querying the AA are const, there's not much difference between an AA of immutable elements and an immutable AA. - Jonathan M DavisShouldn't work.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.
Jan 12 2013
On Sat, Jan 12, 2013 at 04:40:16AM -0800, Jonathan M Davis wrote:On Saturday, January 12, 2013 13:30:47 monarch_dodra wrote:[...]Yeah, like: http://d.puremagic.com/issues/show_bug.cgi?id=3825 http://d.puremagic.com/issues/show_bug.cgi?id=4463 http://d.puremagic.com/issues/show_bug.cgi?id=5234 http://d.puremagic.com/issues/show_bug.cgi?id=6178 http://d.puremagic.com/issues/show_bug.cgi?id=8170What 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.[...] I think issue 4463 shows that ++a[0] produces 1.0 for a double[int], which is totally wrong (it should be nan, if it's even allowed in the first place). The main problem is that the current AA code in aaA.d has no information on the value types, so a lot of things are done by binary instead of by type information. So .init is wrongly conflated with the equivalent of memset(0), opEquals is wrongly conflated with binary equality, etc., etc.. Basically, it's a fractal of brokenness that just happens to work in enough common cases that it barely avoids being unusably frustrating. T -- Skill without imagination is craftsmanship and gives us many useful objects such as wickerwork picnic baskets. Imagination without skill gives us modern art. -- Tom StoppardAs 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.
Jan 12 2013
On 1/12/13 5:51 AM, monarch_dodra wrote:On Saturday, 12 January 2013 at 04:24:01 UTC, deadalnix wrote:But a[0] += 5 can be made to work. AndreiOn Friday, 11 January 2013 at 13:50:33 UTC, 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.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.
Jan 12 2013
On Friday, 11 January 2013 at 08:26:54 UTC, Jonathan M Davis wrote:On Friday, January 11, 2013 08:53:44 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*.How could this be implemented as a library type?I would argue that the fact that x[2] = x[2] + 5; works is a bug. Given some of the weirdness that happens with assigning to AA elements, it doesn't entirely surprise me. For instance, x[2] = funcThatThrows(); results in x[2] holding the init value of x's element type. But I think that it's indicative of problems with the current AA implementation which need to be fixed and not something that we should be trying to emulate with library types.That's my feeling too. I think that if we want to implement AAs as a library type, we first need to eliminate all of the semantics would be impossible to implement in a library. Specificially, I think we need to disallow semantics which are inconsistent with: struct AA { AAImpl impl; ref Value opIndex(Key key); Value opIndexAssign(Value value, Key key); Value opIndexOpAssign(string op)(Value value, Key key); } where the last two create the entry if it doesn't already exist. Those last two should also create a blank AA if it doesn't already exist. Personally I'd be much happier if instead, the blank AA was created at construction, so that AAs would be pure reference types, but at least the semantics are implementable.
Jan 11 2013
On Friday, January 11, 2013 10:03:54 Don wrote:That's my feeling too. I think that if we want to implement AAs as a library type, we first need to eliminate all of the semantics would be impossible to implement in a library.Well, the AAs are _already_ a library type. That's a large part of why they have so many bugs. The transition to a library type was badly done, and we sorely need a new implementation. Also, because the compiler is generating hooks that druntime plugs into, the built-in AAs aren't restricted to quite the same semantics that a user-defined AA type would be, even though the AAs are implemented in druntime instead of the compiler. So, AFAIK, we don't really have a problem with the built-in AAs doing stuff that a library type can't do (that's not even possible at this point, because they're implemented with a library type). Rather, what we need is a new, properly templated solution. But that's a lot of work, and I believe that the last person who attempted it gave up on it beacuse of all the problems that he was running into. - Jonathan M Davis
Jan 11 2013
On Friday, 11 January 2013 at 09:20:13 UTC, Jonathan M Davis wrote:On Friday, January 11, 2013 10:03:54 Don wrote:Unfortunately, that's not true. All that happened was that a template layer was added on top of the built-in implementation. The compiler still knows intimate details about the implementation. The number of hooks from the compiler to the runtime have not decreased; in fact, they have increased. AFAIK there is not a single place in the compiler where coupling between compiler and runtime decreased. There are very many places where it got much more complicated.That's my feeling too. I think that if we want to implement AAs as a library type, we first need to eliminate all of the semantics would be impossible to implement in a library.Well, the AAs are _already_ a library type. That's a large part of why they have so many bugs. The transition to a library type was badly done, and we sorely need a new implementation. Also, because the compiler is generating hooks that druntime plugs into, the built-in AAs aren't restricted to quite the same semantics that a user-defined AA type would be, even though the AAs are implemented in druntime instead of the compiler.So, AFAIK, we don't really have a problem with the built-in AAs doing stuff that a library type can't do (that's not even possible at this point, because they're implemented with a library type).It's not a library type in a meaningful sense. The semantics come almost entirely from the compiler, not from the library. There's almost nothing in the implementation that can be changed without changing the compiler.
Jan 11 2013
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!!! ---I think the last statement is illegal. Because from http://dlang.org/expression.html I extract: The evaluation order of = is implementation defined and it is illegal to depend on it. The compiler should catch these but it cannot in all cases. If the evaluation order was fixed i.e. right-to-left in this case, the code would throw. It also happens that the evaluation may change depending on the optimization flags. So I believe it's an issue of evaluation order. Jens
Jan 11 2013
See also: http://d.puremagic.com/issues/show_bug.cgi?id=3825 Bye, bearophile
Jan 11 2013
"Don" <don nospam.com> wrote in message news:imqicbgjotdtzfgwdeor forum.dlang.org...Consider this code: --- int[int] x; int k = x[2] + 5; // Error, range violation. Makes sense. x[2] = x[2] + 5; // But this works!!! ---Definitely a bug.
Jan 11 2013
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. 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 don't know how opIndex is defined and I cannot find appropriate documentation at http://dlang.org/hash-map.html. But you're right this is odd. Either opIndex throws a RangeError or it creates a value. I would probably go with the C++ approach: creating the value if it does not exist. I.e. making the second statement legal. Then k is int.init + 5. Sorry for my first post. I didn't see it clear. Jens
Jan 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=3825What 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!
Jan 11 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 } =PNote 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.
Jan 11 2013