digitalmars.D - When are associative arrays meant to throw a RangeError?
- Ben Davis (33/33) Feb 17 2012 I can't easily see what the rules are. Specifically, I'm finding:
- Daniel Murphy (7/41) Feb 17 2012 It's 3. It also has the nasty side effect that throwing while evaluatin...
- bearophile (6/8) Feb 17 2012 Maybe related to this thread.
- Daniel Murphy (6/15) Feb 17 2012 Yes, that's the issue I'm talking about. In this case no comments means...
- Jonathan M Davis (4/8) Feb 17 2012 Which should probably be sorted out sooner rather than later given all o...
- Daniel Murphy (4/15) Feb 17 2012 Yeah, but that requires a design that fixes everything, including litera...
- Jonathan M Davis (4/6) Feb 17 2012 Oh, it may not be easy, and it may take some time, but it should probabl...
- H. S. Teoh (12/18) Feb 17 2012 [...]
- Andrei Alexandrescu (3/15) Feb 17 2012 That will change.
- Daniel Murphy (6/10) Feb 18 2012 Another way of looking at this is that the issues with AAs are one of th...
- Ben Davis (13/30) Feb 18 2012 Starting with magic initialisation then...
- Daniel Murphy (22/32) Feb 18 2012 I meant a different magic initialization:
- Ben Davis (7/44) Feb 18 2012 It throws a RangeError. See the examples in my first message in this
- Ben Davis (10/17) Feb 18 2012 I've seen some line-blurring between 'null' and 'empty' for dynamic
- Ben Davis (3/21) Feb 18 2012 Self-correction: I evidently didn't read that array.init returns null
- Andrej Mitrovic (8/8) Feb 18 2012 Returning the default initializer of the value type when the key
- Ben Davis (5/13) Feb 18 2012 Isn't this the kind of situation where you should be using an enum for
- Ben Davis (10/24) Feb 18 2012 Oops, I mean misspelt :)
- Andrej Mitrovic (2/2) Feb 18 2012 Are you familiar with cases where you want regular arrays to return
- Ben Davis (16/18) Feb 18 2012 The front page says D isn't meant to be an orthogonal language :P
- Ben Davis (8/26) Feb 18 2012 To be clear, I'm not too bothered how associative arrays work. My
- Andrej Mitrovic (5/5) Feb 18 2012 Well it's probably too late to change this behavior. Both the sample
- Ben Davis (23/28) Feb 18 2012 That's fine - but let's document it :)
- Andrej Mitrovic (22/23) Feb 18 2012 I've tried making a wrapper type that does this behind the scenes but
- Timon Gehr (4/27) Feb 18 2012 The error is unrelated to your wrapper type.
- Andrej Mitrovic (28/29) Feb 19 2012 Ah, good point. Still can be worked around:
- Jonathan M Davis (4/13) Feb 18 2012 Agreed. The fact that C++ did something like this with std::map was one ...
- Daniel Murphy (16/25) Feb 18 2012 null is not the same thing as an empty array, and I'm not aware of any
- Ben Davis (12/38) Feb 19 2012 So 'null' implicitly turns into { length == 0, ptr == null } when
- Daniel Murphy (3/10) Feb 19 2012 AAs, not Arrays.
- Ben Davis (6/17) Feb 19 2012 Ah, well then I did this test earlier:
- Daniel Murphy (17/36) Feb 19 2012 The call is rewriten to _aa_len(aa) and checks for null.
I can't easily see what the rules are. Specifically, I'm finding: Chunk[][char[4]] chunks; //chunks["DATA"] is an array of Chunk objects, where Chunk is a class. //I'm using this structure to remember positions of chunks in a file format. //These both work: chunks["AAAA"]~=new Chunk(); chunks["BBBB"]=chunks["BBBB"]~new Chunk(); //These all throw RangeErrors Chunk[] tempVar=chunks["CCCC"]~new Chunk(); if (chunks["DDDD"].length!=1) throw new Exception(""); writefln("%s",chunks["EEEE"]); //This works and formats the null as "[]" writefln("%s",cast(Chunk[])null); So, as far as I can tell, chunks[nonexistentKey] throws a RangeError in most cases, including with ~ in general, but not if the ~ is used for an in-place modification. My initial expectation would be that every example above would throw a RangeError (except the one that doesn't use 'chunks' at all). So here are my theories, in the approx order I came up with them: 1=. It's an optimisation accident, and it's meant to throw. 1=. It's a nasty bodge specifically to make assoc arrays usable with modify-assign expressions. 3. It could be a quirk of assignment expression evaluation order. Perhaps the assignment expression FIRST creates an entry in the assoc array using the value type's default value (an empty dynamic array), AND THEN evaluates the RHS, which is able to read the empty array that's just been inserted. Whatever the case, I couldn't find any documentation on the subject. I looked under associative arrays (obviously), and also under assignment expressions, and also under expression statements just for good measure. Would appreciate any comments :) Thanks, Ben :)
Feb 17 2012
It's 3. It also has the nasty side effect that throwing while evaluating the rhs leaves the AA with a default-initialized key. When AA[key] needs to be an lvalue, it gets translated to something like: *_aa_get_lvalue(AA, key) which creates the key if it doesn't exist and returns a reference to it. "Ben Davis" <entheh cantab.net> wrote in message news:jhn2e7$2urh$1 digitalmars.com...I can't easily see what the rules are. Specifically, I'm finding: Chunk[][char[4]] chunks; //chunks["DATA"] is an array of Chunk objects, where Chunk is a class. //I'm using this structure to remember positions of chunks in a file format. //These both work: chunks["AAAA"]~=new Chunk(); chunks["BBBB"]=chunks["BBBB"]~new Chunk(); //These all throw RangeErrors Chunk[] tempVar=chunks["CCCC"]~new Chunk(); if (chunks["DDDD"].length!=1) throw new Exception(""); writefln("%s",chunks["EEEE"]); //This works and formats the null as "[]" writefln("%s",cast(Chunk[])null); So, as far as I can tell, chunks[nonexistentKey] throws a RangeError in most cases, including with ~ in general, but not if the ~ is used for an in-place modification. My initial expectation would be that every example above would throw a RangeError (except the one that doesn't use 'chunks' at all). So here are my theories, in the approx order I came up with them: 1=. It's an optimisation accident, and it's meant to throw. 1=. It's a nasty bodge specifically to make assoc arrays usable with modify-assign expressions. 3. It could be a quirk of assignment expression evaluation order. Perhaps the assignment expression FIRST creates an entry in the assoc array using the value type's default value (an empty dynamic array), AND THEN evaluates the RHS, which is able to read the empty array that's just been inserted. Whatever the case, I couldn't find any documentation on the subject. I looked under associative arrays (obviously), and also under assignment expressions, and also under expression statements just for good measure. Would appreciate any comments :) Thanks, Ben :)
Feb 17 2012
Daniel Murphy:It's 3. It also has the nasty side effect that throwing while evaluating the rhs leaves the AA with a default-initialized key.Maybe related to this thread. A lot of time ago I have added this issue to Bugzilla, I have never received a comment on it, so far: http://d.puremagic.com/issues/show_bug.cgi?id=3825 Bye, bearophile
Feb 17 2012
Yes, that's the issue I'm talking about. In this case no comments means no disagreements. Unfortunately it requires changes to the AA api/codegen to fix, so it will probably be around until we move AAs completely into druntime. "bearophile" <bearophileHUGS lycos.com> wrote in message news:jhn604$5ur$1 digitalmars.com...Daniel Murphy:It's 3. It also has the nasty side effect that throwing while evaluating the rhs leaves the AA with a default-initialized key.Maybe related to this thread. A lot of time ago I have added this issue to Bugzilla, I have never received a comment on it, so far: http://d.puremagic.com/issues/show_bug.cgi?id=3825 Bye, bearophile
Feb 17 2012
On Saturday, February 18, 2012 14:46:21 Daniel Murphy wrote:Yes, that's the issue I'm talking about. In this case no comments means no disagreements. Unfortunately it requires changes to the AA api/codegen to fix, so it will probably be around until we move AAs completely into druntime.Which should probably be sorted out sooner rather than later given all of the bugs involved. - Jonathan M Davis
Feb 17 2012
Yeah, but that requires a design that fixes everything, including literals, template arg deduction, magic initialization etc. "Jonathan M Davis" <jmdavisProg gmx.com> wrote in message news:mailman.514.1329537168.20196.digitalmars-d puremagic.com...On Saturday, February 18, 2012 14:46:21 Daniel Murphy wrote:Yes, that's the issue I'm talking about. In this case no comments means no disagreements. Unfortunately it requires changes to the AA api/codegen to fix, so it will probably be around until we move AAs completely into druntime.Which should probably be sorted out sooner rather than later given all of the bugs involved. - Jonathan M Davis
Feb 17 2012
On Saturday, February 18, 2012 15:13:38 Daniel Murphy wrote:Yeah, but that requires a design that fixes everything, including literals, template arg deduction, magic initialization etc.Oh, it may not be easy, and it may take some time, but it should probably be one of the higher priorities. - Jonathan M Davis
Feb 17 2012
On Fri, Feb 17, 2012 at 09:06:20PM -0800, Jonathan M Davis wrote:On Saturday, February 18, 2012 15:13:38 Daniel Murphy wrote:[...] I agree. AA's are one of the big reasons I chose to learn D. I was very happy to finally have a language that has runtime speed comparable to C++ and has built-in AA's. Sad to say, I've been rather disappointed by the amount of AA related bugs in the current implementation. T -- MS Windows: 64-bit overhaul of 32-bit extensions and a graphical shell for a 16-bit patch to an 8-bit operating system originally coded for a 4-bit microprocessor, written by a 2-bit company that can't stand 1-bit of competition.Yeah, but that requires a design that fixes everything, including literals, template arg deduction, magic initialization etc.Oh, it may not be easy, and it may take some time, but it should probably be one of the higher priorities.
Feb 17 2012
On 2/18/12 12:39 AM, H. S. Teoh wrote:On Fri, Feb 17, 2012 at 09:06:20PM -0800, Jonathan M Davis wrote:That will change. AndreiOn Saturday, February 18, 2012 15:13:38 Daniel Murphy wrote:[...] I agree. AA's are one of the big reasons I chose to learn D. I was very happy to finally have a language that has runtime speed comparable to C++ and has built-in AA's. Sad to say, I've been rather disappointed by the amount of AA related bugs in the current implementation.Yeah, but that requires a design that fixes everything, including literals, template arg deduction, magic initialization etc.Oh, it may not be easy, and it may take some time, but it should probably be one of the higher priorities.
Feb 17 2012
"H. S. Teoh" <hsteoh quickfur.ath.cx> wrote in message news:mailman.521.1329547094.20196.digitalmars-d puremagic.com...I agree. AA's are one of the big reasons I chose to learn D. I was very happy to finally have a language that has runtime speed comparable to C++ and has built-in AA's. Sad to say, I've been rather disappointed by the amount of AA related bugs in the current implementation.Another way of looking at this is that the issues with AAs are one of the bigger implementation problems D has these days - not compiler crashes, wrong code generation, thousands of ice bugs with reasonable looking code... The implementation has come a long way in the last couple of years.
Feb 18 2012
Starting with magic initialisation then... Is it vital that e[nonexistentKey] throw a RangeError, or could it just always return the type's default value if the key is absent? If you change that, then you can make assignment evaluate the RHS fully before even creating the LHS entry, and you won't in the process break the common case where people want to go count[key]++; or array[key]~=element; without worrying about whether it's the first time for that key or not. Users who want to know if the entry is there could then use 'in' (once it's fixed). On 18/02/2012 04:13, Daniel Murphy wrote:Yeah, but that requires a design that fixes everything, including literals, template arg deduction, magic initialization etc. "Jonathan M Davis"<jmdavisProg gmx.com> wrote in message news:mailman.514.1329537168.20196.digitalmars-d puremagic.com...On Saturday, February 18, 2012 14:46:21 Daniel Murphy wrote:Yes, that's the issue I'm talking about. In this case no comments means no disagreements. Unfortunately it requires changes to the AA api/codegen to fix, so it will probably be around until we move AAs completely into druntime.Which should probably be sorted out sooner rather than later given all of the bugs involved. - Jonathan M Davis
Feb 18 2012
"Ben Davis" <entheh cantab.net> wrote in message news:jho2mf$2a1t$1 digitalmars.com...Starting with magic initialisation then...I meant a different magic initialization: int[int] aa = null; aa[3] = 7; // aa is magically not null any moreIs it vital that e[nonexistentKey] throw a RangeError, or could it just always return the type's default value if the key is absent?This is what it does.If you change that, then you can make assignment evaluate the RHS fully before even creating the LHS entry, and you won't in the process break the common case where people want to go count[key]++; or array[key]~=element; without worrying about whether it's the first time for that key or not.This problem is just a bug in code generation from what I can tell, because lowering it manually results in the rhs being evaluated first. import std.stdio; int* getp() { writeln("1"); return new int; } void main() { *getp() += { writeln("2"); return 1; }(); } prints: 2 1 I have no idea where this is happening in the compiler.
Feb 18 2012
On 18/02/2012 13:22, Daniel Murphy wrote:"Ben Davis"<entheh cantab.net> wrote in message news:jho2mf$2a1t$1 digitalmars.com...It throws a RangeError. See the examples in my first message in this thread. I'm asking if changing the semantics to NOT throw a RangeError would be an option.Starting with magic initialisation then...I meant a different magic initialization: int[int] aa = null; aa[3] = 7; // aa is magically not null any moreIs it vital that e[nonexistentKey] throw a RangeError, or could it just always return the type's default value if the key is absent?This is what it does.Interesting - my gut feeling is that associative arrays are syntactic sugar and are being rewritten to use other constructs, and that rewriting is implementing a different execution order.If you change that, then you can make assignment evaluate the RHS fully before even creating the LHS entry, and you won't in the process break the common case where people want to go count[key]++; or array[key]~=element; without worrying about whether it's the first time for that key or not.This problem is just a bug in code generation from what I can tell, because lowering it manually results in the rhs being evaluated first. import std.stdio; int* getp() { writeln("1"); return new int; } void main() { *getp() += { writeln("2"); return 1; }(); } prints: 2 1 I have no idea where this is happening in the compiler.
Feb 18 2012
On 18/02/2012 13:22, Daniel Murphy wrote:"Ben Davis"<entheh cantab.net> wrote in message news:jho2mf$2a1t$1 digitalmars.com...I've seen some line-blurring between 'null' and 'empty' for dynamic arrays (non-associative). Specifically, I read that array.init returns null for both static and dynamic, but I think I also read that a dynamic array's default value is the empty array. I also observed that null~[1] == [1], and I wondered if actually 'null' becomes an empty array when cast to dynamic array and they're effectively the same thing. If I'm right, then the same could be true for assoc arrays - that 'null' cast to an assoc array type becomes an empty assoc array. Which would explain the magic you're seeing.Starting with magic initialisation then...I meant a different magic initialization: int[int] aa = null; aa[3] = 7; // aa is magically not null any more
Feb 18 2012
Self-correction: I evidently didn't read that array.init returns null for static arrays. But the point holds for dynamic ones. On 18/02/2012 19:15, Ben Davis wrote:On 18/02/2012 13:22, Daniel Murphy wrote:"Ben Davis"<entheh cantab.net> wrote in message news:jho2mf$2a1t$1 digitalmars.com...I've seen some line-blurring between 'null' and 'empty' for dynamic arrays (non-associative). Specifically, I read that array.init returns null for both static and dynamic, but I think I also read that a dynamic array's default value is the empty array. I also observed that null~[1] == [1], and I wondered if actually 'null' becomes an empty array when cast to dynamic array and they're effectively the same thing. If I'm right, then the same could be true for assoc arrays - that 'null' cast to an assoc array type becomes an empty assoc array. Which would explain the magic you're seeing.Starting with magic initialisation then...I meant a different magic initialization: int[int] aa = null; aa[3] = 7; // aa is magically not null any more
Feb 18 2012
Returning the default initializer of the value type when the key doesn't exist is a bad idea. Consider an integer, it's .init value is 0. If I want to check if a value of a key is zero I could easily end up with a silent bug: int[string] aa; aa["foobar"] = 5; if (aa["fobar"] == 0) { } // will always be true else { }
Feb 18 2012
On 18/02/2012 20:54, Andrej Mitrovic wrote:Returning the default initializer of the value type when the key doesn't exist is a bad idea. Consider an integer, it's .init value is 0. If I want to check if a value of a key is zero I could easily end up with a silent bug: int[string] aa; aa["foobar"] = 5; if (aa["fobar"] == 0) { } // will always be true else { }Isn't this the kind of situation where you should be using an enum for the key type? Or indeed just creating a struct or a class to hold the values you need? Especially as remove() already gives you 'silent bugs' if the key is misspelled.
Feb 18 2012
On 18/02/2012 21:42, Ben Davis wrote:On 18/02/2012 20:54, Andrej Mitrovic wrote:Oops, I mean misspelt :) Another possible situation where you could already get a silent bug: aa["foobar"] = 5; ... aa["fobar"] = 6; ... if (aa["foobar"]==5) {...} Are you familiar with cases where an associative array is definitely the right tool for the job and you have a high risk of typos?Returning the default initializer of the value type when the key doesn't exist is a bad idea. Consider an integer, it's .init value is 0. If I want to check if a value of a key is zero I could easily end up with a silent bug: int[string] aa; aa["foobar"] = 5; if (aa["fobar"] == 0) { } // will always be true else { }Isn't this the kind of situation where you should be using an enum for the key type? Or indeed just creating a struct or a class to hold the values you need? Especially as remove() already gives you 'silent bugs' if the key is misspelled.
Feb 18 2012
Are you familiar with cases where you want regular arrays to return Type.init when you go out of bounds?
Feb 18 2012
On 18/02/2012 22:57, Andrej Mitrovic wrote:Are you familiar with cases where you want regular arrays to return Type.init when you go out of bounds?The front page says D isn't meant to be an orthogonal language :P If you want orthogonality, then associative arrays will have to work something like this: int[string] stuff; stuff.addKey("a"); stuff.addKey("b"); stuff.addKey("d"); stuff.addKey("e"); stuff["a"]=0; stuff["b"]=1; stuff["c"]=2; //error writefln("%s",stuff["d"]); writefln("%s",stuff["e"]); writefln("%s",stuff["f"]); //error Do you want to do that?
Feb 18 2012
To be clear, I'm not too bothered how associative arrays work. My proposal was merely a means by which the following currently working code: stuff[previouslyNonexistentKey]++; could continue to work without relying on a current implementation quirk-possibly-bug. If you want to change it not to work and make people's existing code crash, you can :) On 18/02/2012 23:08, Ben Davis wrote:On 18/02/2012 22:57, Andrej Mitrovic wrote:Are you familiar with cases where you want regular arrays to return Type.init when you go out of bounds?The front page says D isn't meant to be an orthogonal language :P If you want orthogonality, then associative arrays will have to work something like this: int[string] stuff; stuff.addKey("a"); stuff.addKey("b"); stuff.addKey("d"); stuff.addKey("e"); stuff["a"]=0; stuff["b"]=1; stuff["c"]=2; //error writefln("%s",stuff["d"]); writefln("%s",stuff["e"]); writefln("%s",stuff["f"]); //error Do you want to do that?
Feb 18 2012
Well it's probably too late to change this behavior. Both the sample on the hash page and TDPL itself shows the usage of that trick. Btw, if you really want Type.init if the key doesn't exist you can use the get method: Chunk[] tempVar = chunks.get("CCCC", null) ~ new Chunk();
Feb 18 2012
On 18/02/2012 23:33, Andrej Mitrovic wrote:Well it's probably too late to change this behavior. Both the sample on the hash page and TDPL itself shows the usage of that trick.That's fine - but let's document it :) A few things seem to be missing: - You get a RangeError for reading a nonexistent key; - However, you can safely write a[k]=a[k]+1 or a[k]++, because if a[k] doesn't exist, then a[k]=b sets a[k] to the default value first before evaluating b. (This is a special case for assoc array assignments, not for other assignments.) - .init property for assoc arrays returns null. - For dynamic arrays and assoc arrays, 'null' is an empty array, so you don't have to worry about null crashes like with objects. Here are the tests I did to confirm the above: writefln("%s",cast(int[string])null); //prints "[]" int[string] assoc=null; writefln("%s",assoc.length); //prints 0 writefln("%s",(cast(int[string])null).length); //breaks the compiler for me :P but not an important use case int[] dyn=null; writefln("%s",dyn.length); //prints 0Btw, if you really want Type.init if the key doesn't exist you can use the get method: Chunk[] tempVar = chunks.get("CCCC", null) ~ new Chunk();Yes, good to know! I see it's in the docs too. Thanks :)
Feb 18 2012
On 2/19/12, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Chunk[] tempVar = chunks.get("CCCC", null) ~ new Chunk();I've tried making a wrapper type that does this behind the scenes but it won't work: struct Hash(Key, Val) { Val[Key] aa; Val opIndex(Key key) { return aa.get(key, Val.init); } alias aa this; } class Chunk { } alias Hash!(string, Chunk) ChunkHash; void main() { ChunkHash chunks; Chunk[] tempVar = chunks["CCCC"] ~ new Chunk(); } test.d(20): Error: incompatible types for ((chunks.opIndex("CCCC")) ~ (new Chunk)): 'test.Chunk' and 'test.Chunk' Very odd..
Feb 18 2012
On 02/19/2012 12:40 AM, Andrej Mitrovic wrote:On 2/19/12, Andrej Mitrovic<andrej.mitrovich gmail.com> wrote:The error is unrelated to your wrapper type. static assert(!is(typeof(1~1))); Concatenation only works if at least one of the types is an array.Chunk[] tempVar = chunks.get("CCCC", null) ~ new Chunk();I've tried making a wrapper type that does this behind the scenes but it won't work: struct Hash(Key, Val) { Val[Key] aa; Val opIndex(Key key) { return aa.get(key, Val.init); } alias aa this; } class Chunk { } alias Hash!(string, Chunk) ChunkHash; void main() { ChunkHash chunks; Chunk[] tempVar = chunks["CCCC"] ~ new Chunk(); } test.d(20): Error: incompatible types for ((chunks.opIndex("CCCC")) ~ (new Chunk)): 'test.Chunk' and 'test.Chunk' Very odd..
Feb 18 2012
On 2/19/12, Timon Gehr <timon.gehr gmx.ch> wrote:Concatenation only works if at least one of the types is an array.Ah, good point. Still can be worked around: struct Hash(Key, Val) { struct WrapVal(Val) { Val val; auto opCat(Val rhs) { return [val] ~ rhs; } alias val this; } alias WrapVal!Val ValType; ValType[Key] aa; ValType opIndex(Key key) { return aa.get(key, ValType.init); } alias aa this; } class Chunk { } alias Hash!(string, Chunk) ChunkHash; void main() { ChunkHash chunks; Chunk[] tempVar = chunks["CCCC"] ~ new Chunk(); }
Feb 19 2012
On Saturday, February 18, 2012 21:54:52 Andrej Mitrovic wrote:Returning the default initializer of the value type when the key doesn't exist is a bad idea. Consider an integer, it's .init value is 0. If I want to check if a value of a key is zero I could easily end up with a silent bug: int[string] aa; aa["foobar"] = 5; if (aa["fobar"] == 0) { } // will always be true else { }Agreed. The fact that C++ did something like this with std::map was one of its big mistakes IMHO. - Jonathan M Davis
Feb 18 2012
"Ben Davis" <entheh cantab.net> wrote in message news:jhotcm$13ag$1 digitalmars.com...I've seen some line-blurring between 'null' and 'empty' for dynamic arrays (non-associative). Specifically, I read that array.init returns null for both static and dynamic, but I think I also read that a dynamic array's default value is the empty array. I also observed that null~[1] == [1], and I wondered if actually 'null' becomes an empty array when cast to dynamic array and they're effectively the same thing.null is not the same thing as an empty array, and I'm not aware of any situations where null will implicitly turn into one. null == { length == 0, ptr == null } empty == { length == 0, ptr != null } To make an empty array you (generally) need to allocate memory for it, and having this happen implicitly would be a problem.If I'm right, then the same could be true for assoc arrays - that 'null' cast to an assoc array type becomes an empty assoc array. Which would explain the magic you're seeing.This is not what's happening. With the lvalue AA lookup, the call turns into this: *_d_aaGet(&AA, keyinformation ...) = value; Because it passes a pointer to the actual AA variable, if the AA doesn't exist it is created. All of the rvalue AA methods behave the same for null and empty AAs. Except for this magic initialization, AAs behave the same as classes - ie a reference type.
Feb 18 2012
On 19/02/2012 03:31, Daniel Murphy wrote:"Ben Davis"<entheh cantab.net> wrote in message news:jhotcm$13ag$1 digitalmars.com...So 'null' implicitly turns into { length == 0, ptr == null } when implicitly cast to the array type (and then it behaves like an empty array in many situations). That needs documenting :) Coming from any of C, C++ or Java, you would think of null (or NULL) as a pointer to 0, which will crash if you try to dereference it in any way - so the fact that (null array).length is valid and gives 0 is not obvious! Thanks for explaining it to me in any case :)I've seen some line-blurring between 'null' and 'empty' for dynamic arrays (non-associative). Specifically, I read that array.init returns null for both static and dynamic, but I think I also read that a dynamic array's default value is the empty array. I also observed that null~[1] == [1], and I wondered if actually 'null' becomes an empty array when cast to dynamic array and they're effectively the same thing.null is not the same thing as an empty array, and I'm not aware of any situations where null will implicitly turn into one. null == { length == 0, ptr == null } empty == { length == 0, ptr != null } To make an empty array you (generally) need to allocate memory for it, and having this happen implicitly would be a problem.... Cool!If I'm right, then the same could be true for assoc arrays - that 'null' cast to an assoc array type becomes an empty assoc array. Which would explain the magic you're seeing.This is not what's happening. With the lvalue AA lookup, the call turns into this: *_d_aaGet(&AA, keyinformation ...) = value; Because it passes a pointer to the actual AA variable, if the AA doesn't exist it is created. All of the rvalue AA methods behave the same for null and empty AAs.Except for this magic initialization, AAs behave the same as classes - ie a reference type.That's not quite true, because 'length' is passed around by value alongside the reference, leading to semantics you could never reproduce with classes, unless I'm mistaken.
Feb 19 2012
"Ben Davis" <entheh cantab.net> wrote in message news:jhr0qf$24sj$1 digitalmars.com...On 19/02/2012 03:31, Daniel Murphy wrote:AAs, not Arrays.Except for this magic initialization, AAs behave the same as classes - ie a reference type.That's not quite true, because 'length' is passed around by value alongside the reference, leading to semantics you could never reproduce with classes, unless I'm mistaken.
Feb 19 2012
On 19/02/2012 15:05, Daniel Murphy wrote:"Ben Davis"<entheh cantab.net> wrote in message news:jhr0qf$24sj$1 digitalmars.com...Ah, well then I did this test earlier: int[string] assoc=null; writefln("%s",assoc.length); //prints 0 Why did that work?On 19/02/2012 03:31, Daniel Murphy wrote:AAs, not Arrays.Except for this magic initialization, AAs behave the same as classes - ie a reference type.That's not quite true, because 'length' is passed around by value alongside the reference, leading to semantics you could never reproduce with classes, unless I'm mistaken.
Feb 19 2012
The call is rewriten to _aa_len(aa) and checks for null. This can almost be done with a normal class, except the compiler inserts a null check into each member function, iirc. I guess that's another bit of magic that can't be handled simply. It can still be done with a struct: struct AAPimpl { AAImpl aa; size_t length() property { if (!aa) return 0; return aa.length(); } } I expect something like this will end up being the solution. "Ben Davis" <entheh cantab.net> wrote in message news:jhr4d1$2b4n$1 digitalmars.com...On 19/02/2012 15:05, Daniel Murphy wrote:"Ben Davis"<entheh cantab.net> wrote in message news:jhr0qf$24sj$1 digitalmars.com...Ah, well then I did this test earlier: int[string] assoc=null; writefln("%s",assoc.length); //prints 0 Why did that work?On 19/02/2012 03:31, Daniel Murphy wrote:AAs, not Arrays.Except for this magic initialization, AAs behave the same as classes - ie a reference type.That's not quite true, because 'length' is passed around by value alongside the reference, leading to semantics you could never reproduce with classes, unless I'm mistaken.
Feb 19 2012