digitalmars.D - Behaviour of AAs after initialization
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (13/13) Aug 07 2014 AAs are (like regular dynamic arrays) initialized to `null`. On
- H. S. Teoh via Digitalmars-d (28/40) Aug 07 2014 [...]
- Puming (13/62) Aug 07 2014 for most of the new users the WAT part is actually here :-)
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (5/10) Aug 07 2014 I'm afraid that copying is too general. This would trigger on
- H. S. Teoh via Digitalmars-d (19/30) Aug 07 2014 It really just needs a standard function in druntime that does this.
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (17/55) Aug 07 2014 It should take aa by ref, but then the default value needs to go.
- Puming (18/56) Aug 07 2014 That is what I'm using now...
- Puming (6/18) Aug 07 2014 I don't know the details, I was thinking that copy only happens
- Andrei Alexandrescu (4/18) Aug 07 2014 One function we could and should use is one that makes an AA that is
- Ary Borenszweig (6/24) Aug 07 2014 It still won't be intuitive for newcomers or for anyone not knowing that...
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (5/35) Aug 07 2014 It cannot be done, unfortunately. Think about struct members.
- Jonathan M Davis (4/7) Aug 07 2014 https://issues.dlang.org/show_bug.cgi?id=10535
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (7/17) Aug 07 2014 I agree, we already have this behaviour with regular arrays,
AAs are (like regular dynamic arrays) initialized to `null`. On first modification (i.e. assignment of an element), memory is allocated and the AA variable updated to point at it. However, is there a guarantee that AAs are never reallocated once they are initialized, i.e. is it then safe to assume that changes made through a copy of an AA variable (e.g. pass by value) are visible through the original variable? The current implementation behaves like this, but the language reference does not mention it, AFAICS. I'd like to amend the documentation if this behaviour is reliable. This is the thread where the question came up: http://forum.dlang.org/post/qnllglkrrgfdpwbmvwsp forum.dlang.org http://forum.dlang.org/post/tguaxwqijnkqszghauia forum.dlang.org
Aug 07 2014
On Thu, Aug 07, 2014 at 11:46:48AM +0000, via Digitalmars-d wrote:AAs are (like regular dynamic arrays) initialized to `null`. On first modification (i.e. assignment of an element), memory is allocated and the AA variable updated to point at it. However, is there a guarantee that AAs are never reallocated once they are initialized, i.e. is it then safe to assume that changes made through a copy of an AA variable (e.g. pass by value) are visible through the original variable? The current implementation behaves like this, but the language reference does not mention it, AFAICS. I'd like to amend the documentation if this behaviour is reliable.[...] I'm not the one to make the decision, but I'd vote for codifying this behaviour in the language reference. From what I understand, at least, it seems that this is the intention anyway, and the current implementation certainly suggests so. Otherwise, it leads to strange awkward semantics where passing AA's around may sometimes change the original "view" of it, and sometimes not. It's really just the .init value of null which causes odd behaviour with empty AA's. Fun fact: void changeAA(int[string] aa) { aa["a"] = 123; } // Null AA: int[string] aa1; // null assert(aa1.length == 0); changeAA(aa1); // no effect assert(aa1.length == 0); // Empty but non-null AA: int[string] aa2; // null aa2["a"] = 0; aa2.remove("a"); // empty but non-null assert(aa2.length == 0); changeAA(aa2); // has effect! assert(aa2.length == 1); // WAT :-) T -- People tell me that I'm skeptical, but I don't believe it.
Aug 07 2014
On Thursday, 7 August 2014 at 16:53:24 UTC, H. S. Teoh via Digitalmars-d wrote:On Thu, Aug 07, 2014 at 11:46:48AM +0000, via Digitalmars-d wrote:for most of the new users the WAT part is actually here :-) In all other occations AA behaves just like a reference type: You want to changeAA with assignment and you get an empty(null) AA, you should be able to change it. refering to an null AA and not wanting to modify it is not a common case. So I'd like to suggest a rule here similar to what assignment does to null AA: If someone refers to an uninitialized null AA ( in implementation, that maybe, a copy of a null AA struct happens), allocate it first.AAs are (like regular dynamic arrays) initialized to `null`. On first modification (i.e. assignment of an element), memory is allocated and the AA variable updated to point at it. However, is there a guarantee that AAs are never reallocated once they are initialized, i.e. is it then safe to assume that changes made through a copy of an AA variable (e.g. pass by value) are visible through the original variable? The current implementation behaves like this, but the language reference does not mention it, AFAICS. I'd like to amend the documentation if this behaviour is reliable.[...] I'm not the one to make the decision, but I'd vote for codifying this behaviour in the language reference. From what I understand, at least, it seems that this is the intention anyway, and the current implementation certainly suggests so. Otherwise, it leads to strange awkward semantics where passing AA's around may sometimes change the original "view" of it, and sometimes not. It's really just the .init value of null which causes odd behaviour with empty AA's. Fun fact: void changeAA(int[string] aa) { aa["a"] = 123; } // Null AA: int[string] aa1; // null assert(aa1.length == 0); changeAA(aa1); // no effectassert(aa1.length == 0); // Empty but non-null AA: int[string] aa2; // null aa2["a"] = 0; aa2.remove("a"); // empty but non-null assert(aa2.length == 0); changeAA(aa2); // has effect! assert(aa2.length == 1); // WAT :-) T
Aug 07 2014
On Thursday, 7 August 2014 at 17:35:46 UTC, Puming wrote:So I'd like to suggest a rule here similar to what assignment does to null AA: If someone refers to an uninitialized null AA ( in implementation, that maybe, a copy of a null AA struct happens), allocate it first.I'm afraid that copying is too general. This would trigger on just about any access. It would also make copying AAs more expensive. I believe a standardized method for initializing an AA is more likely to be accepted.
Aug 07 2014
On Thu, Aug 07, 2014 at 05:42:28PM +0000, via Digitalmars-d wrote:On Thursday, 7 August 2014 at 17:35:46 UTC, Puming wrote:It really just needs a standard function in druntime that does this. Perhaps something like this: // in object.di T initialize(T : V[K], V, K)(T aa = null) { aa[K.init] = V.init; aa.remove(K.init); // or if you like, encapsulate this in aaA.d and just // allocate Impl without the add/delete hackery. return aa; } // in user code auto aa = initialize!(string[int]); The name / syntax is up for bikeshedding, but the idea is pretty simple. Make a druntime function that allocates the initial empty AA, and make that function accessible to user code. T -- Some ideas are so stupid that only intellectuals could believe them. -- George OrwellSo I'd like to suggest a rule here similar to what assignment does to null AA: If someone refers to an uninitialized null AA ( in implementation, that maybe, a copy of a null AA struct happens), allocate it first.I'm afraid that copying is too general. This would trigger on just about any access. It would also make copying AAs more expensive. I believe a standardized method for initializing an AA is more likely to be accepted.
Aug 07 2014
On Thursday, 7 August 2014 at 18:05:15 UTC, H. S. Teoh via Digitalmars-d wrote:On Thu, Aug 07, 2014 at 05:42:28PM +0000, via Digitalmars-d wrote:It should take aa by ref, but then the default value needs to go. (Not a problem, just add a second overload.) T initialize(T : V[K], V, K)(ref T aa) { aa[K.init] = V.init; aa.remove(K.init); return aa; } T initialize(T : V[K], V, K)() { T aa; return initialize!T(aa); } // in user code auto aa = initialize!(string[int]); string[int] bb; bb.initialize();On Thursday, 7 August 2014 at 17:35:46 UTC, Puming wrote:It really just needs a standard function in druntime that does this. Perhaps something like this: // in object.di T initialize(T : V[K], V, K)(T aa = null) { aa[K.init] = V.init; aa.remove(K.init); // or if you like, encapsulate this in aaA.d and just // allocate Impl without the add/delete hackery. return aa; } // in user code auto aa = initialize!(string[int]); The name / syntax is up for bikeshedding, but the idea is pretty simple. Make a druntime function that allocates the initial empty AA, and make that function accessible to user code. TSo I'd like to suggest a rule here similar to what assignment does to null AA: If someone refers to an uninitialized null AA ( in implementation, that maybe, a copy of a null AA struct happens), allocate it first.I'm afraid that copying is too general. This would trigger on just about any access. It would also make copying AAs more expensive. I believe a standardized method for initializing an AA is more likely to be accepted.
Aug 07 2014
On Thursday, 7 August 2014 at 18:05:15 UTC, H. S. Teoh via Digitalmars-d wrote:On Thu, Aug 07, 2014 at 05:42:28PM +0000, via Digitalmars-d wrote:That is what I'm using now... But the implementation (assignement plus remove) would look weird. People were asking for ``` string[int] aa = []; // or string[int] aa = [:]; ``` is it hard to implement, or confict with other language features? But if we have a initialize mechanism, it would be better to deprecate the 'silent allocate on first assignment', because people would easily forgot to init the aa, and then refer to it, and the compiler being silent on this, only finding that the original one is not affected later, contrary to their intentions. If we want: "You must initialize an AA before refering to it", the compiler should enforce that.On Thursday, 7 August 2014 at 17:35:46 UTC, Puming wrote:It really just needs a standard function in druntime that does this. Perhaps something like this: // in object.di T initialize(T : V[K], V, K)(T aa = null) { aa[K.init] = V.init; aa.remove(K.init); // or if you like, encapsulate this in aaA.d and just // allocate Impl without the add/delete hackery. return aa; } // in user code auto aa = initialize!(string[int]); The name / syntax is up for bikeshedding, but the idea is pretty simple. Make a druntime function that allocates the initial empty AA, and make that function accessible to user code. TSo I'd like to suggest a rule here similar to what assignment does to null AA: If someone refers to an uninitialized null AA ( in implementation, that maybe, a copy of a null AA struct happens), allocate it first.I'm afraid that copying is too general. This would trigger on just about any access. It would also make copying AAs more expensive. I believe a standardized method for initializing an AA is more likely to be accepted.
Aug 07 2014
On Thursday, 7 August 2014 at 17:42:29 UTC, Marc Schütz wrote:On Thursday, 7 August 2014 at 17:35:46 UTC, Puming wrote:I don't know the details, I was thinking that copy only happens when an assignment (`auto aa2 = aa1;`) happens :-(So I'd like to suggest a rule here similar to what assignment does to null AA: If someone refers to an uninitialized null AA ( in implementation, that maybe, a copy of a null AA struct happens), allocate it first.I'm afraid that copying is too general. This would trigger on just about any access.It would also make copying AAs more expensive.all writes to AA already do the same check, right? but if you mean all reads will also incur copying, then I agree it would be more expensive.I believe a standardized method for initializing an AA is more likely to be accepted.
Aug 07 2014
On 8/7/14, 10:35 AM, Puming wrote:On Thursday, 7 August 2014 at 16:53:24 UTC, H. S. Teoh via Digitalmars-dOne function we could and should use is one that makes an AA that is empty but not null. Right now one needs to use goofy methods such as adding and then removing a key. -- AndreiIt's really just the .init value of null which causes odd behaviour with empty AA's. Fun fact: void changeAA(int[string] aa) { aa["a"] = 123; } // Null AA: int[string] aa1; // null assert(aa1.length == 0); changeAA(aa1); // no effectfor most of the new users the WAT part is actually here :-)
Aug 07 2014
On 8/7/14, 3:57 PM, Andrei Alexandrescu wrote:On 8/7/14, 10:35 AM, Puming wrote:It still won't be intuitive for newcomers or for anyone not knowing that function. I would invert it: declaring an associative array makes it non-null. Then you can choose, with a function, to initialize to null. This would follow the principle of least surprise.On Thursday, 7 August 2014 at 16:53:24 UTC, H. S. Teoh via Digitalmars-dOne function we could and should use is one that makes an AA that is empty but not null. Right now one needs to use goofy methods such as adding and then removing a key. -- AndreiIt's really just the .init value of null which causes odd behaviour with empty AA's. Fun fact: void changeAA(int[string] aa) { aa["a"] = 123; } // Null AA: int[string] aa1; // null assert(aa1.length == 0); changeAA(aa1); // no effectfor most of the new users the WAT part is actually here :-)
Aug 07 2014
On Thursday, 7 August 2014 at 19:27:10 UTC, Ary Borenszweig wrote:On 8/7/14, 3:57 PM, Andrei Alexandrescu wrote:It cannot be done, unfortunately. Think about struct members. Their init value needs to be known at compile time, so the best we could achieve would be to have all instances point to the same AA by default, which is worse than what we have now.On 8/7/14, 10:35 AM, Puming wrote:It still won't be intuitive for newcomers or for anyone not knowing that function. I would invert it: declaring an associative array makes it non-null. Then you can choose, with a function, to initialize to null. This would follow the principle of least surprise.On Thursday, 7 August 2014 at 16:53:24 UTC, H. S. Teoh via Digitalmars-dOne function we could and should use is one that makes an AA that is empty but not null. Right now one needs to use goofy methods such as adding and then removing a key. -- AndreiIt's really just the .init value of null which causes odd behaviour with empty AA's. Fun fact: void changeAA(int[string] aa) { aa["a"] = 123; } // Null AA: int[string] aa1; // null assert(aa1.length == 0); changeAA(aa1); // no effectfor most of the new users the WAT part is actually here :-)
Aug 07 2014
On Thursday, 7 August 2014 at 18:57:15 UTC, Andrei Alexandrescu wrote:One function we could and should use is one that makes an AA that is empty but not null. Right now one needs to use goofy methods such as adding and then removing a key. -- Andreihttps://issues.dlang.org/show_bug.cgi?id=10535 - Jonathan M Davis
Aug 07 2014
On Thursday, 7 August 2014 at 16:53:24 UTC, H. S. Teoh via Digitalmars-d wrote:I'm not the one to make the decision, but I'd vote for codifying this behaviour in the language reference. From what I understand, at least, it seems that this is the intention anyway, and the current implementation certainly suggests so. Otherwise, it leads to strange awkward semantics where passing AA's around may sometimes change the original "view" of it, and sometimes not.I agree, we already have this behaviour with regular arrays, that's why Dragos thought AA act the same. But for arrays it is out of necessity, because slice store (and expose in the API!) a pointer to the data, while AAs can afford to act differently, because their implementation is opaque.
Aug 07 2014