digitalmars.D - AA revisited
- David Medlock (45/45) Jul 12 2005 I have to say the AA changes are confusing to say the least.
- Shammah Chancellor (32/77) Jul 12 2005 Some of the code you mentioned has already been deprecated.
- David Medlock (14/126) Jul 12 2005 Where is this documented?
- Chris Sauls (18/20) Jul 13 2005 There is no exception thrown if aa[newkey] is an lvalue. I have tested ...
- Ben Hinkle (19/53) Jul 12 2005 Once the bug that you found with & is fixed the above can be rewritten a...
- David Medlock (28/65) Jul 12 2005 That's beginning to look like C++.
- Ben Hinkle (14/78) Jul 12 2005 I'm not sure what you mean by "old way". Can you be more specific?
- David Medlock (3/11) Jul 12 2005 Since you see a benefit, please tell me what it is. It still escapes me...
- Ben Hinkle (9/20) Jul 12 2005 * more intuitive for most users (ie - those not assuming AAs are stl::ma...
- David Medlock (31/37) Jul 12 2005 Ben Hinkle wrote:
- Andrew Fedoniouk (6/43) Jul 12 2005 Agree.
- Ben Hinkle (6/14) Jul 12 2005 I don't actually mind returning init on failure but my preference is
- Andrew Fedoniouk (18/33) Jul 12 2005 :) Well, Canada prefer different enum values {Friend, Almost,
- Concerned (13/47) Jul 12 2005 I am beginning to like
- Regan Heath (13/26) Jul 12 2005 import std.stdio;
- =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= (9/17) Jul 13 2005 I was arguing for the same thing, back when it inserted a "zero" entry
- Regan Heath (6/20) Jul 13 2005 Or, using one lookup:
- =?ISO-8859-15?Q?Anders_F_Bj=F6rklund?= (8/17) Jul 13 2005 There are two down-sides to this try/catch approach:
- Nick (12/19) Jul 13 2005 I, for one, agree with you. It can also help catch lots of bugs, such as...
- David Medlock (6/23) Jul 13 2005 If that is a constant, it should be stored in one. In cases where it is...
- sq (3/5) Jul 13 2005 And so should he change it to allow "if (expr);" and "if (a=b)" ?
- David Medlock (11/23) Jul 13 2005 Both are syntactical lint-like check built into the compiler.
- Shammah Chancellor (6/33) Jul 12 2005 Not to mention we have some context dependant syntax going on there with...
- Shammah Chancellor (5/25) Jul 12 2005 This will no longer work as lookup[key] will throw an exception since of
- Ben Hinkle (4/17) Jul 12 2005 lookup[key] does not throw if the result is expected to be an lvalue. Th...
- Shammah Chancellor (15/33) Jul 12 2005 Hrm.. It didn't say that in the changelog, but i'll believe you. (See th...
- Ben Hinkle (7/49) Jul 12 2005 The details about AAs are still largely undocumented, that's true. Walte...
- David Medlock (87/87) Jul 12 2005 Here is an example of code which was elegant, and now must be messied up...
- Andrew Fedoniouk (20/20) Jul 12 2005 Generally speaking effective AA implementation should have three methods...
- Andrew Fedoniouk (9/9) Jul 12 2005 Forgot to mention:
- Charles Hixson (16/43) Jul 12 2005 While that's a good minimal set, I don't think it's an optimal
- Andrew Fedoniouk (7/50) Jul 12 2005 Yep. Nice to have but is quite rare I guess.
- me (24/25) Jul 12 2005 The current semantics helps coders guard against mistakes. Rather than a...
I have to say the AA changes are confusing to say the least. With classes using 'lookup[key]' would create an (key,value) *entry* but not an *object*, so you still must check for null and create if necessary. The amount of code here is basically the same whether you use *in* or just aa[key]. Since you are using an object reference, you can change the object in the AA directly and there is no real need for pointers. We now have 3 possibilities for AA which return classes: 1. Key is not in the AA, must check for this or Exception thrown. 2. Key is in the AA, but the reference stored there is null. 3. Key is in the AA, reference points to an object. void CallMethod( MyClass c, int key, MyClass[int] lookup ) { MyClass* ptr = (key in lookup); if ( ptr is null ) lookup[key] = new MyClass(); MyClass ref = lookup[key]; if ( ref is null ) { ref = new MyClass(); lookup[key]= ref; } ref.Method(); } This is starting to make C++ look clean. Structs on the other hand are supposed to be implicitly created. They are *values* not objects. So now we have 2 coding idioms/paradigms for structs: 1. Implicitly create when declared in code. struct S { int n = 99; void callme(){ n++ ; } } S var1, var2, var3; // created as declared var1.callme(); // no problem, I want the defaults, I defined them 2. Struct value in an AA. S[int] lookup; S[100].callme() ; //Now this statement requires 7-10 lines of code. All this because someone was concerned with a double lookup the *first time* an object isnt found in the AA? Semantical madness over something that isnt even a real deficiency. Premature optimization is a root cause of bad programming, because you end up making hard to follow code in attempts to optimize before you even know what is slow. This is premature optimization at the *language level*. -DavidM
Jul 12 2005
In article <db0p66$vfj$1 digitaldaemon.com>, David Medlock says...I have to say the AA changes are confusing to say the least. With classes using 'lookup[key]' would create an (key,value) *entry* but not an *object*, so you still must check for null and create if necessary. The amount of code here is basically the same whether you use *in* or just aa[key]. Since you are using an object reference, you can change the object in the AA directly and there is no real need for pointers. We now have 3 possibilities for AA which return classes: 1. Key is not in the AA, must check for this or Exception thrown. 2. Key is in the AA, but the reference stored there is null. 3. Key is in the AA, reference points to an object. void CallMethod( MyClass c, int key, MyClass[int] lookup ) { MyClass* ptr = (key in lookup); if ( ptr is null ) lookup[key] = new MyClass(); MyClass ref = lookup[key]; if ( ref is null ) { ref = new MyClass(); lookup[key]= ref; } ref.Method(); } This is starting to make C++ look clean. Structs on the other hand are supposed to be implicitly created. They are *values* not objects. So now we have 2 coding idioms/paradigms for structs: 1. Implicitly create when declared in code. struct S { int n = 99; void callme(){ n++ ; } } S var1, var2, var3; // created as declared var1.callme(); // no problem, I want the defaults, I defined them 2. Struct value in an AA. S[int] lookup; S[100].callme() ; //Now this statement requires 7-10 lines of code. All this because someone was concerned with a double lookup the *first time* an object isnt found in the AA? Semantical madness over something that isnt even a real deficiency. Premature optimization is a root cause of bad programming, because you end up making hard to follow code in attempts to optimize before you even know what is slow. This is premature optimization at the *language level*. -DavidMSome of the code you mentioned has already been deprecated. aa[key] now throws an exception if it does not exist. Thus aa[newkey] = new Class is now invalid. You must instead call aa.Add(key) (See changes for DMD 126: Now throws an ArrayBoundsError if accessing an associative array with a key that is not already in the array. Previously, the key would be added to the array. ) Thus your code would change to something like this :try { : foo = aa[key]; :} catch ( Exception e ) { : foo = aa.Add(key, new MyClass()); :} finally { : foo.Method(); :} Error handling sucks. Personally I think AA syntax is starting to transcend integral types. Since when does an integer throw an exception? I'm all for making AA's a part of phobos and separating them from the language syntax. Also, these silly functions properties that are accessed like members are very confusing. Like this: void HelloWorld(int foo) { writeln("Hi world, I have %d foos", foo); } int foo; foo.HelloWorld(); ^-- AAARGGGH!!! Confusion ensues! I'd like to see this syntax go away too. It's not that much effort for people who want to extend the integer type to type this: HelloWorld(foo); (which is less typing By the way, notice the lack of the extra period) Which is how it belongs since it's not part of the integer type, and you can't find it in the class file for integer.
Jul 12 2005
Shammah Chancellor wrote:In article <db0p66$vfj$1 digitaldaemon.com>, David Medlock says...Where is this documented? http://www.digitalmars.com/d/arrays.html#associative I see examples of int[char[]] b; // associative array b of ints that are // indexed by an array of characters. // The KeyType is char[] b["hello"] = 3; // set value associated with key "hello" to 3I have to say the AA changes are confusing to say the least. With classes using 'lookup[key]' would create an (key,value) *entry* but not an *object*, so you still must check for null and create if necessary. The amount of code here is basically the same whether you use *in* or just aa[key]. Since you are using an object reference, you can change the object in the AA directly and there is no real need for pointers. We now have 3 possibilities for AA which return classes: 1. Key is not in the AA, must check for this or Exception thrown. 2. Key is in the AA, but the reference stored there is null. 3. Key is in the AA, reference points to an object. void CallMethod( MyClass c, int key, MyClass[int] lookup ) { MyClass* ptr = (key in lookup); if ( ptr is null ) lookup[key] = new MyClass(); MyClass ref = lookup[key]; if ( ref is null ) { ref = new MyClass(); lookup[key]= ref; } ref.Method(); } This is starting to make C++ look clean. Structs on the other hand are supposed to be implicitly created. They are *values* not objects. So now we have 2 coding idioms/paradigms for structs: 1. Implicitly create when declared in code. struct S { int n = 99; void callme(){ n++ ; } } S var1, var2, var3; // created as declared var1.callme(); // no problem, I want the defaults, I defined them 2. Struct value in an AA. S[int] lookup; S[100].callme() ; //Now this statement requires 7-10 lines of code. All this because someone was concerned with a double lookup the *first time* an object isnt found in the AA? Semantical madness over something that isnt even a real deficiency. Premature optimization is a root cause of bad programming, because you end up making hard to follow code in attempts to optimize before you even know what is slow. This is premature optimization at the *language level*. -DavidMSome of the code you mentioned has already been deprecated. aa[key] now throws an exception if it does not exist. Thus aa[newkey] = new Class is now invalid. You must instead call aa.Add(key)(See changes for DMD 126: Now throws an ArrayBoundsError if accessing an associative array with a key that is not already in the array. Previously, the key would be added to the array. ) Thus your code would change to something like this :try { : foo = aa[key]; :} catch ( Exception e ) { : foo = aa.Add(key, new MyClass()); :} finally { : foo.Method(); :} Error handling sucks. Personally I think AA syntax is starting to transcend integral types. Since when does an integer throw an exception? I'm all for making AA's a part of phobos and separating them from the language syntax.That is better than the previous semantics? Compared to what? Java? I thought D was supposed to be attracting C/C++ programmers. Now we have checked exceptions?Also, these silly functions properties that are accessed like members are very confusing. Like this: void HelloWorld(int foo) { writeln("Hi world, I have %d foos", foo); } int foo; foo.HelloWorld(); ^-- AAARGGGH!!! Confusion ensues! I'd like to see this syntax go away too. It's not that much effort for people who want to extend the integer type to type this: HelloWorld(foo); (which is less typing By the way, notice the lack of the extra period) Which is how it belongs since it's not part of the integer type, and you can't find it in the class file for integer.I agree you can go overboard with built in methods, however for templates they are more valuable because they can be overloaded in a class, whereas you cannot overload a specialized operator like *in*.
Jul 12 2005
Shammah Chancellor wrote:aa[key] now throws an exception if it does not exist. Thus aa[newkey] = new Class is now invalid. You must instead call aa.Add(key)There is no exception thrown if aa[newkey] is an lvalue. I have tested this with DMD 0.128, which also errored when I tried using this .Add(key) syntax you mention. As far as I know, the way to add a key to an AA is still: -- Chris Sauls PS: Sample test program.
Jul 13 2005
void CallMethod( MyClass c, int key, MyClass[int] lookup ) { MyClass* ptr = (key in lookup); if ( ptr is null ) lookup[key] = new MyClass(); MyClass ref = lookup[key]; if ( ref is null ) { ref = new MyClass(); lookup[key]= ref; } ref.Method(); }Once the bug that you found with & is fixed the above can be rewritten as void CallMethod( int key, MyClass[int] lookup ) { MyClass* ptr = &lookup[key]; if ( !*ptr ) *ptr = new MyClass; *ptr.Method(); }This is starting to make C++ look clean.Again assuming the & bug is fixed the C++ code map<A,B> x; B& ref = x[a]; can be rewritten to the D code B[A] x; B* ref = &x[a]; That is about as simple as one can expect.Structs on the other hand are supposed to be implicitly created. They are *values* not objects. So now we have 2 coding idioms/paradigms for structs: 1. Implicitly create when declared in code. struct S { int n = 99; void callme(){ n++ ; } } S var1, var2, var3; // created as declared var1.callme(); // no problem, I want the defaults, I defined them 2. Struct value in an AA. S[int] lookup; S[100].callme() ; //Now this statement requires 7-10 lines of code. All this because someone was concerned with a double lookup the *first time* an object isnt found in the AA? Semantical madness over something that isnt even a real deficiency. Premature optimization is a root cause of bad programming, because you end up making hard to follow code in attempts to optimize before you even know what is slow. This is premature optimization at the *language level*. -DavidMSomewhere I had read that C++ chose the lookup-and-insert behavior because throwing an exception was too slow and has spotty support. In any case I know BS said that operator[] returns a reference because returning the value is too slow. So there are a couple of C++ examples where performance drove the design.
Jul 12 2005
Ben Hinkle wrote:That's beginning to look like C++. Besides you haven't shown how that is superior to the old way. What if I want value semantics and not call by reference? (Which is the whole point of using a struct vs a class ) B* ref = &x[a]; B temp = ref[0]; // now I can use my local copy and reinsert it if needed // versus B temp = x[a]; The point is the change as made the code *more* complex, with zero benefits whatsoever. If the double lookup is a non-issue because it only matters when the value is null (ie the first time). If you look up the *in stinks* thread, Matthew starts with complaints that the in operator returned a pointer. I actually agree with his sentiment, foreach(collections), object references(classes) and out parameters(for structs and other values) are superior to pointers. http://www.digitalmars.com/d/archives/digitalmars/D/18450.html Now pointers are *required*, and the code to access structures is longer(my example) or messy(your example)!! The better alternative to *in* is a method: B value; if ( x.get( in a, out value ) ) { value.dostuff(); } That is as simple as it gets, no pointers, can still use the create semantics, and if 'x' is a template alias, the x can implement .get(...) and still function with the don't create semantics. I can't overload your example. -DavidMvoid CallMethod( MyClass c, int key, MyClass[int] lookup ) { MyClass* ptr = (key in lookup); if ( ptr is null ) lookup[key] = new MyClass(); MyClass ref = lookup[key]; if ( ref is null ) { ref = new MyClass(); lookup[key]= ref; } ref.Method(); }Once the bug that you found with & is fixed the above can be rewritten as void CallMethod( int key, MyClass[int] lookup ) { MyClass* ptr = &lookup[key]; if ( !*ptr ) *ptr = new MyClass; *ptr.Method(); }This is starting to make C++ look clean.Again assuming the & bug is fixed the C++ code map<A,B> x; B& ref = x[a]; can be rewritten to the D code B[A] x; B* ref = &x[a]; That is about as simple as one can expect.
Jul 12 2005
"David Medlock" <noone nowhere.com> wrote in message news:db0vub$15ot$1 digitaldaemon.com...Ben Hinkle wrote:I'm not sure what you mean by "old way". Can you be more specific?That's beginning to look like C++. Besides you haven't shown how that is superior to the old way.void CallMethod( MyClass c, int key, MyClass[int] lookup ) { MyClass* ptr = (key in lookup); if ( ptr is null ) lookup[key] = new MyClass(); MyClass ref = lookup[key]; if ( ref is null ) { ref = new MyClass(); lookup[key]= ref; } ref.Method(); }Once the bug that you found with & is fixed the above can be rewritten as void CallMethod( int key, MyClass[int] lookup ) { MyClass* ptr = &lookup[key]; if ( !*ptr ) *ptr = new MyClass; *ptr.Method(); }This is starting to make C++ look clean.Again assuming the & bug is fixed the C++ code map<A,B> x; B& ref = x[a]; can be rewritten to the D code B[A] x; B* ref = &x[a]; That is about as simple as one can expect.What if I want value semantics and not call by reference? (Which is the whole point of using a struct vs a class ) B* ref = &x[a]; B temp = ref[0]; // now I can use my local copy and reinsert it if needed // versus B temp = x[a]; The point is the change as made the code *more* complex, with zero benefits whatsoever. If the double lookup is a non-issue because it only matters when the value is null (ie the first time).umm - "zero benefit"? whatever... I agree it would be nice to have an explicit method to insert-and-lookup when that is what one wants as was discussed in the posts on the bug thread about &.If you look up the *in stinks* thread, Matthew starts with complaints that the in operator returned a pointer. I actually agree with his sentiment, foreach(collections), object references(classes) and out parameters(for structs and other values) are superior to pointers. http://www.digitalmars.com/d/archives/digitalmars/D/18450.html Now pointers are *required*, and the code to access structures is longer(my example) or messy(your example)!! The better alternative to *in* is a method: B value; if ( x.get( in a, out value ) ) { value.dostuff(); } That is as simple as it gets, no pointers, can still use the create semantics, and if 'x' is a template alias, the x can implement .get(...) and still function with the don't create semantics.Various posts (I included it in my API requests) had what you call "get" by the name "contains" (though to be honest I've forgotten the details). I personally don't have anything against pointers, though, so the current 'in' is ok with me.I can't overload your example.I, too, would like more overload control than what is available today. See the posts about opIndexMutable and such started by Kevin Bealer, for example.
Jul 12 2005
Ben Hinkle wrote:"David Medlock" <noone nowhere.com> wrote in messageSince you see a benefit, please tell me what it is. It still escapes me. -DavidMThe point is the change as made the code *more* complex, with zero benefits whatsoever. If the double lookup is a non-issue because it only matters when the value is null (ie the first time).umm - "zero benefit"? whatever...
Jul 12 2005
"David Medlock" <noone nowhere.com> wrote in message news:db11io$174u$1 digitaldaemon.com...Ben Hinkle wrote:* more intuitive for most users (ie - those not assuming AAs are stl::map) Python (which errors) * means regular rvalue lookup does not modify the array which means reading is thread-safe * consistent with dynamic and static arrays where requesting a value not in the array throws"David Medlock" <noone nowhere.com> wrote in messageSince you see a benefit, please tell me what it is. It still escapes me. -DavidMThe point is the change as made the code *more* complex, with zero benefits whatsoever. If the double lookup is a non-issue because it only matters when the value is null (ie the first time).umm - "zero benefit"? whatever...
Jul 12 2005
Ben Hinkle wrote: > * more intuitive for most users (ie - those not assuming AAs are stl::map) This is subjective of course, but aren't most D ppl coming from C++? See my post about call-by-value vs call-by-reference. We added 1 new condition for struct/values and 1 new condition for classes. More intuitive for a total of 5 conditions vs 3 before? IMO declaring: MyClass[int] aa; MyClass c = aa[100]; should give me the default for an uninitialized MyClass object, null. I don't think an Exception is warranted, because I expect some values won't exist yet, but that does not make the key invalid!Python (which errors)non-classes. Their containers are themselves classes, which could be implemented in D also. Python errors , while Lua returns null: -- lua code a = lookup[100] or createObject(); -- very succinct -- Plus python has the get(..) method we discussed. Not much Python code try...catches around the lookup I'd bet.* means regular rvalue lookup does not modify the array which means reading is thread-safeThread safe code should be in a thread safe class/module. I don't think thread safety should dictate the lowest common denominator for a language which is already pretty wide open, thread-safety wise.* consistent with dynamic and static arrays where requesting a value not in the array throwsThrowing exceptions are supposed to be for unusual circumstances, not normal execution. I would hope D isn't moving towards checked Exceptions. In any case, I sincerely hope Walter adds a get()/lookup() builtin. Thanks for the points, Ben. Please do not mistake my passion for a pragmatic D as anything else. -DavidM
Jul 12 2005
"David Medlock" <noone nowhere.com> wrote in message news:db15vt$1aqu$1 digitaldaemon.com...Ben Hinkle wrote: > * more intuitive for most users (ie - those not assuming AAs are stl::map) This is subjective of course, but aren't most D ppl coming from C++? See my post about call-by-value vs call-by-reference. We added 1 new condition for struct/values and 1 new condition for classes. More intuitive for a total of 5 conditions vs 3 before? IMO declaring: MyClass[int] aa; MyClass c = aa[100]; should give me the default for an uninitialized MyClass object, null. I don't think an Exception is warranted, because I expect some values won't exist yet, but that does not make the key invalid!Agree.and Python (which errors)non-classes. Their containers are themselves classes, which could be implemented in D also. Python errors , while Lua returns null: -- lua code a = lookup[100] or createObject(); -- very succinct -- Plus python has the get(..) method we discussed. Not much Python code try...catches around the lookup I'd bet.* means regular rvalue lookup does not modify the array which means reading is thread-safeThread safe code should be in a thread safe class/module. I don't think thread safety should dictate the lowest common denominator for a language which is already pretty wide open, thread-safety wise.Agree. "key does not exist" is always expected result for AA. And constant/immutable AAs must have only 'in' (or immutable get) method available to caller.* consistent with dynamic and static arrays where requesting a value not in the array throwsThrowing exceptions are supposed to be for unusual circumstances, not normal execution. I would hope D isn't moving towards checked Exceptions.In any case, I sincerely hope Walter adds a get()/lookup() builtin. Thanks for the points, Ben. Please do not mistake my passion for a pragmatic D as anything else. -DavidM
Jul 12 2005
I don't actually mind returning init on failure but my preference is throwing. I think Matthew was the biggest proponent of throwing - but my memory is vague. Here's a silly example but imagine an AA indexed by country with enum {Invade, Sanctions} and you look up, say, Iraq and no-one had thought about what to do about Iraq yet. That might actually explain a few things. :-PAgree. "key does not exist" is always expected result for AA. And constant/immutable AAs must have only 'in' (or immutable get) method available to caller.* consistent with dynamic and static arrays where requesting a value not in the array throwsThrowing exceptions are supposed to be for unusual circumstances, not normal execution. I would hope D isn't moving towards checked Exceptions.
Jul 12 2005
"Ben Hinkle" <bhinkle mathworks.com> wrote in message news:db19ia$1ead$1 digitaldaemon.com...:) Well, Canada prefer different enum values {Friend, Almost, NotYet}otherwise example is good. There are two distinct situations: 1) immutable AA - List of keywords in compiler. It shouldn't be such entities as opIndex and opIndexAssign at all. Right? 2) dynamic AA - E.g. word counting: Both opIndex and opIndexAssign should be there. dictionary[word]++; in wc2.d looked more elegantly than if (!(word in dictionary)) dictionary[word] = 0; else ++dictionary[word]; isn't it? Andrew.I don't actually mind returning init on failure but my preference is throwing. I think Matthew was the biggest proponent of throwing - but my memory is vague. Here's a silly example but imagine an AA indexed by country with enum {Invade, Sanctions} and you look up, say, Iraq and no-one had thought about what to do about Iraq yet. That might actually explain a few things. :-PAgree. "key does not exist" is always expected result for AA. And constant/immutable AAs must have only 'in' (or immutable get) method available to caller.* consistent with dynamic and static arrays where requesting a value not in the array throwsThrowing exceptions are supposed to be for unusual circumstances, not normal execution. I would hope D isn't moving towards checked Exceptions.
Jul 12 2005
I am beginning to like int K[char[] : loose] and int U[char[] : strict] K["notpresentkey"] would return "" that is the init (but not create element) and U["notpresentkey"] would rise an error as in http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/26519 By the way the word count example (that you mentioned) is a very good one. And also if I wanted to know how many words "apple" there where, then 0 (the init) would be the right answer. Thank you! In article <db1erq$1jca$1 digitaldaemon.com>, Andrew Fedoniouk says..."Ben Hinkle" <bhinkle mathworks.com> wrote in message news:db19ia$1ead$1 digitaldaemon.com...:) Well, Canada prefer different enum values {Friend, Almost, NotYet}otherwise example is good. There are two distinct situations: 1) immutable AA - List of keywords in compiler. It shouldn't be such entities as opIndex and opIndexAssign at all. Right? 2) dynamic AA - E.g. word counting: Both opIndex and opIndexAssign should be there. dictionary[word]++; in wc2.d looked more elegantly than if (!(word in dictionary)) dictionary[word] = 0; else ++dictionary[word]; isn't it? Andrew.I don't actually mind returning init on failure but my preference is throwing. I think Matthew was the biggest proponent of throwing - but my memory is vague. Here's a silly example but imagine an AA indexed by country with enum {Invade, Sanctions} and you look up, say, Iraq and no-one had thought about what to do about Iraq yet. That might actually explain a few things. :-PAgree. "key does not exist" is always expected result for AA. And constant/immutable AAs must have only 'in' (or immutable get) method available to caller.* consistent with dynamic and static arrays where requesting a value not in the array throwsThrowing exceptions are supposed to be for unusual circumstances, not normal execution. I would hope D isn't moving towards checked Exceptions.
Jul 12 2005
On Tue, 12 Jul 2005 15:05:46 -0700, Andrew Fedoniouk <news terrainformatica.com> wrote:There are two distinct situations: 1) immutable AA - List of keywords in compiler. It shouldn't be such entities as opIndex and opIndexAssign at all. Right? 2) dynamic AA - E.g. word counting: Both opIndex and opIndexAssign should be there. dictionary[word]++; in wc2.d looked more elegantly than if (!(word in dictionary)) dictionary[word] = 0; else ++dictionary[word]; isn't it?import std.stdio; void main() { int[char[]] dictionary; dictionary["regan"]++; writefln(dictionary["regan"]); } works fine. The change only effects: int i = dictionary["regan"]; usage an an rvalue (is that the correct terminology?) Regan
Jul 12 2005
David Medlock wrote:IMO declaring: MyClass[int] aa; MyClass c = aa[100]; should give me the default for an uninitialized MyClass object, null. I don't think an Exception is warranted, because I expect some values won't exist yet, but that does not make the key invalid!I was arguing for the same thing, back when it inserted a "zero" entry instead of throwing a strange "OutOfBounds" exception - like it is now. The current behaviour has the implicit side affect of defining an AA to only the current keys, which I think is a very sad definition of it ? Anyway, the work-around to this is still the same as it was before... Do a double lookup, once to see if the key exists and twice to get it. MyClass c = (100 in aa) ? aa[100] : int.init; --anders
Jul 13 2005
On Wed, 13 Jul 2005 11:26:43 +0200, Anders F Björklund <afb algonet.se> wrote:David Medlock wrote:Or, using one lookup: try { c = aa[100]; } catch(ArrayBoundsError e) { c = new MyClass(); } ReganIMO declaring: MyClass[int] aa; MyClass c = aa[100]; should give me the default for an uninitialized MyClass object, null. I don't think an Exception is warranted, because I expect some values won't exist yet, but that does not make the key invalid!I was arguing for the same thing, back when it inserted a "zero" entry instead of throwing a strange "OutOfBounds" exception - like it is now. The current behaviour has the implicit side affect of defining an AA to only the current keys, which I think is a very sad definition of it ? Anyway, the work-around to this is still the same as it was before... Do a double lookup, once to see if the key exists and twice to get it. MyClass c = (100 in aa) ? aa[100] : int.init;
Jul 13 2005
Regan Heath wrote:There are two down-sides to this try/catch approach: 1) I think it might be slower, but haven't tested that... 2) It won't work in -release mode, no ArrayBoundsErrors ? In fact, with GDC I even get an access error when trying to retrieve a key outside the ones I've explicitly defined. So I think I'll stick with the old bandaid, thank you :-) --andersAnyway, the work-around to this is still the same as it was before... Do a double lookup, once to see if the key exists and twice to get it. MyClass c = (100 in aa) ? aa[100] : int.init;Or, using one lookup: try { c = aa[100]; } catch(ArrayBoundsError e) { c = new MyClass(); }
Jul 13 2005
In article <db12ll$17tq$1 digitaldaemon.com>, Ben Hinkle says...* more intuitive for most users (ie - those not assuming AAs are stl::map) Python (which errors) * means regular rvalue lookup does not modify the array which means reading is thread-safe * consistent with dynamic and static arrays where requesting a value not in the array throwsI, for one, agree with you. It can also help catch lots of bugs, such as typos, ie: somelist["height"] = 10; .. int height = somelist["heigth"]; // Should complain rather than return zero This is why, after thinking about it, I am really not in favor of adding more special cases for adding elements, such as & or accessing struct members. What we need instead (IMO) is a clearer way for the programmer to specify whether (s)he wants to only read existing elements, or to add them if they doesn't exist. Nick
Jul 13 2005
Nick wrote:In article <db12ll$17tq$1 digitaldaemon.com>, Ben Hinkle says...If that is a constant, it should be stored in one. In cases where it is computed or passed as a parameter it will already be in a variable. In any case, I don't think Walter should limit the capabilities of the language based around possible programmer typos. -DavidM* more intuitive for most users (ie - those not assuming AAs are stl::map) Python (which errors) * means regular rvalue lookup does not modify the array which means reading is thread-safe * consistent with dynamic and static arrays where requesting a value not in the array throwsI, for one, agree with you. It can also help catch lots of bugs, such as typos, ie: somelist["height"] = 10; .. int height = somelist["heigth"]; // Should complain rather than return zero
Jul 13 2005
In article <db302m$629$2 digitaldaemon.com>, David Medlock says... [snip]In any case, I don't think Walter should limit the capabilities of the language based around possible programmer typos.And so should he change it to allow "if (expr);" and "if (a=b)" ?
Jul 13 2005
sq wrote:In article <db302m$629$2 digitaldaemon.com>, David Medlock says... [snip]Both are syntactical lint-like check built into the compiler. Nothing in the spec disallows the first one, its not allowed in Walter's implementation. I can prove the second is lint-checking too: bool b = ( a = true ); if ( a = true ) writefln("Hello"); on the first line ( a = true ) evaluates to a boolean. On the second line the same expression does not.... Your example is syntax, the discussion is semantics. -DavidMIn any case, I don't think Walter should limit the capabilities of the language based around possible programmer typos.And so should he change it to allow "if (expr);" and "if (a=b)" ?
Jul 13 2005
That's beginning to look like C++. Besides you haven't shown how that is superior to the old way. What if I want value semantics and not call by reference? (Which is the whole point of using a struct vs a class ) B* ref = &x[a]; B temp = ref[0]; // now I can use my local copy and reinsert it if needed // versus B temp = x[a]; The point is the change as made the code *more* complex, with zero benefits whatsoever. If the double lookup is a non-issue because it only matters when the value is null (ie the first time). If you look up the *in stinks* thread, Matthew starts with complaints that the in operator returned a pointer. I actually agree with his sentiment, foreach(collections), object references(classes) and out parameters(for structs and other values) are superior to pointers. http://www.digitalmars.com/d/archives/digitalmars/D/18450.html Now pointers are *required*, and the code to access structures is longer(my example) or messy(your example)!! The better alternative to *in* is a method: B value; if ( x.get( in a, out value ) ) { value.dostuff(); } That is as simple as it gets, no pointers, can still use the create semantics, and if 'x' is a template alias, the x can implement .get(...) and still function with the don't create semantics. I can't overload your example. -DavidMNot to mention we have some context dependant syntax going on there with the in keyword being used for different things in different places. IE: int foo( in int a, inout int[int] foo ) { MyClass *arg = a in foo; } See the different use? I vote to make AA it's own class....
Jul 12 2005
In article <db0ucs$14fp$1 digitaldaemon.com>, Ben Hinkle says...This will no longer work as lookup[key] will throw an exception since of creating the key since DMD 0.126. If Add will replace existing values, or if there is a Replace method then you could still simplify it to something very close to what you have.void CallMethod( MyClass c, int key, MyClass[int] lookup ) { MyClass* ptr = (key in lookup); if ( ptr is null ) lookup[key] = new MyClass(); MyClass ref = lookup[key]; if ( ref is null ) { ref = new MyClass(); lookup[key]= ref; } ref.Method(); }Once the bug that you found with & is fixed the above can be rewritten as void CallMethod( int key, MyClass[int] lookup ) { MyClass* ptr = &lookup[key]; if ( !*ptr ) *ptr = new MyClass; *ptr.Method(); }
Jul 12 2005
lookup[key] does not throw if the result is expected to be an lvalue. The problem is that there is a bug in dmd that the & operator isn't among those operators that are flagged as expecting an lvalue. See David's earlier post in this newsgroup and see my post about it in the bugs newsgroup.Once the bug that you found with & is fixed the above can be rewritten as void CallMethod( int key, MyClass[int] lookup ) { MyClass* ptr = &lookup[key]; if ( !*ptr ) *ptr = new MyClass; *ptr.Method(); }This will no longer work as lookup[key] will throw an exception since of creating the key since DMD 0.126. If Add will replace existing values, or if there is a Replace method then you could still simplify it to something very close to what you have.
Jul 12 2005
In article <db118h$16qm$1 digitaldaemon.com>, Ben Hinkle says...Hrm.. It didn't say that in the changelog, but i'll believe you. (See the changelog for DMD 0.126) Shouldn't this work: void CallMethod( MyClass c, int key, MyClass[int] lookup ) { MyClass *ref = key in lookup ; MyClass ptr; if ( !ref || !*ref ) lookup[key] = ptr = new MyClass(); else ptr = *ref; ptr.Method(); } I can't really see a better solution. Adding or ignoring if exists doesn't seem good. IE: aa.CreateIfNotExists( key, new MyClass() ); This will always create a new MyClass even if there is no new key made.lookup[key] does not throw if the result is expected to be an lvalue. The problem is that there is a bug in dmd that the & operator isn't among those operators that are flagged as expecting an lvalue. See David's earlier post in this newsgroup and see my post about it in the bugs newsgroup.Once the bug that you found with & is fixed the above can be rewritten as void CallMethod( int key, MyClass[int] lookup ) { MyClass* ptr = &lookup[key]; if ( !*ptr ) *ptr = new MyClass; *ptr.Method(); }This will no longer work as lookup[key] will throw an exception since of creating the key since DMD 0.126. If Add will replace existing values, or if there is a Replace method then you could still simplify it to something very close to what you have.
Jul 12 2005
"Shammah Chancellor" <Shammah_member pathlink.com> wrote in message news:db14mg$19qo$1 digitaldaemon.com...In article <db118h$16qm$1 digitaldaemon.com>, Ben Hinkle says...The details about AAs are still largely undocumented, that's true. Walter hasn't been very vocal about where he intends to go with AAs but that isn't unusual.Hrm.. It didn't say that in the changelog, but i'll believe you. (See the changelog for DMD 0.126)lookup[key] does not throw if the result is expected to be an lvalue. The problem is that there is a bug in dmd that the & operator isn't among those operators that are flagged as expecting an lvalue. See David's earlier post in this newsgroup and see my post about it in the bugs newsgroup.Once the bug that you found with & is fixed the above can be rewritten as void CallMethod( int key, MyClass[int] lookup ) { MyClass* ptr = &lookup[key]; if ( !*ptr ) *ptr = new MyClass; *ptr.Method(); }This will no longer work as lookup[key] will throw an exception since of creating the key since DMD 0.126. If Add will replace existing values, or if there is a Replace method then you could still simplify it to something very close to what you have.Shouldn't this work: void CallMethod( MyClass c, int key, MyClass[int] lookup ) { MyClass *ref = key in lookup ; MyClass ptr; if ( !ref || !*ref ) lookup[key] = ptr = new MyClass(); else ptr = *ref; ptr.Method(); } I can't really see a better solution. Adding or ignoring if exists doesn't seem good. IE: aa.CreateIfNotExists( key, new MyClass() ); This will always create a new MyClass even if there is no new key made.Correct - that will work today. However I still hope Walter makes &lookup[key] insert if not present.
Jul 12 2005
Here is an example of code which was elegant, and now must be messied up because of the changes in AA(at bottom). My choices now are to A) switch everyhing to pointers and use &keys[...] B) add functions which create when needed in the arrays C) use a function to add all the keys on startup (Not feasible, as keys are updated as they are queried) I think the code below is fairly elegant, but it will not be under the new AA. // This code uses the glfw framework to check Keys and Mouse Movement import glfw; import std.stdio; public int MouseX, MouseY, MouseZ, MouseDX, MouseDY, MouseDZ; enum Mouse { Left=GLFW_MOUSE_BUTTON_LEFT, Right=GLFW_MOUSE_BUTTON_RIGHT, Middle=GLFW_MOUSE_BUTTON_MIDDLE }; enum Key { F1=GLFW_KEY_F1, F2=GLFW_KEY_F2, F3=GLFW_KEY_F3, F4=GLFW_KEY_F4 }; private: struct keystate { bool down = false; bool changed = false; bool updated = false; bool released() { return (!down) && changed; } bool pressed() { return down && changed; } protected void update( bool isdown ) { changed = ( down != isdown ); down = isdown; updated = true; } } keystate[ int ] keys; keystate[ int ] mbutton; // mouse buttons are updated each Update, so just return the state keystate getmouse( int btn ){ return mbutton[ btn ]; } keystate getkey( int keycode ) { keystate st = keys[keycode]; if ( !st.updated ) { st.update( glfwGetKey( keycode )==GLFW_PRESS ); keys[keycode] = st; } return st; } public: void ClearInput() { foreach( int code; keys.keys ) delete keys[code]; foreach( int code; mbutton.keys ) delete mbutton[code]; MouseDX = MouseDY = MouseDZ = 0; } void CheckInput() { foreach( int kcode; keys.keys ) (kcode in keys).updated = false; glfwPollEvents(); int x, y, z; mbutton[Mouse.Left].update( glfwGetMouseButton( Mouse.Left )==GLFW_PRESS ); mbutton[Mouse.Middle].update( glfwGetMouseButton( Mouse.Middle )==GLFW_PRESS ); mbutton[Mouse.Right].update( glfwGetMouseButton( Mouse.Right )==GLFW_PRESS ); z = glfwGetMouseWheel(); glfwGetMousePos( &x, &y ); MouseDX = x - MouseX; MouseDY = y - MouseY; MouseDZ = z - MouseZ; MouseX = x; MouseY = y; MouseZ = z; } // key was just pressed bool KeyPressed( int code ) { return getkey( code ).pressed; } // key was just released bool KeyReleased( int code ) { return getkey( code ).released;} // key is currently down bool KeyDown( int code ) { return getkey( code ).down ; } // key is currently up bool KeyUp( int code ) { return getkey( code ).down==false; } // mouse has moved since last update bool MouseMove() { return (MouseDX!=0) || (MouseDY!=0) || (MouseDZ!=0 ); } bool MouseDown(int btn) { return getmouse( btn ).down; } bool MouseUp( int btn ) { return false==getmouse( btn ).down; } bool MouseReleased( int btn ) { return getmouse(btn).released; } bool MousePressed( int btn ) { return getmouse(btn).pressed; }
Jul 12 2005
Generally speaking effective AA implementation should have three methods: bool aa.search( key , &value ); -- const (immutable) method. -- D: key in aa; aa.insert( key, value ); -- mutable method, 1) replaces value under the key if it is there or 2) inserts new key.value pair into the assosiation if does not exist. -- D: aa[key] = value; value aa.get( key, function(key,value) ctor ); -- mutable method, if value is not in collection calls ctor to construct value for the new pair. always returns initialized value. -- ??? This is canonically full set of operations. In presence of default initializers for types in D last 'get' method could be substituted by x = aa[key] with silent construction of new value if needed. Not so convenient but reasonable compromise. Andrew.
Jul 12 2005
Forgot to mention: To be consistent with Ben's requirement: "* consistent with dynamic and static arrays where requesting a value not in the array throws" AA should have read-only equivalent - read-only AA. it throws if key is not in collection. 'insert' method shall be disabled and and any attempt to call must be prevented at compile time. Andrew.
Jul 12 2005
Andrew Fedoniouk wrote:Generally speaking effective AA implementation should have three methods: bool aa.search( key , &value ); -- const (immutable) method. -- D: key in aa; aa.insert( key, value ); -- mutable method, 1) replaces value under the key if it is there or 2) inserts new key.value pair into the assosiation if does not exist. -- D: aa[key] = value; value aa.get( key, function(key,value) ctor ); -- mutable method, if value is not in collection calls ctor to construct value for the new pair. always returns initialized value. -- ??? This is canonically full set of operations. In presence of default initializers for types in D last 'get' method could be substituted by x = aa[key] with silent construction of new value if needed. Not so convenient but reasonable compromise. Andrew.While that's a good minimal set, I don't think it's an optimal set. An optimal set should have two more: 1) aa.create( key, value ); -- immutable method, 1) the key must not exist. Inserts new key.value pair into the association. Or fails (either returning null or throwing an error. 2) value aa.get( key); -- immutable method, fails if value is not in collection. Although this is a good set of operators, the syntax isn't pretty. One characteristic that a good syntax would have is that it would always be possible to guarantee that the object could be read without being changed. For that reason the expression, e.g.: if (aa["item"]) should be guaranteed to NOT insert "item" into aa. Syntax is always tricky, and a clear syntax is difficult.
Jul 12 2005
"Charles Hixson" <charleshixsn earthlink.net> wrote in message news:db1v3l$1v8v$1 digitaldaemon.com...Andrew Fedoniouk wrote:Yep. Nice to have but is quite rare I guess. PS: it is definitely mutable.Generally speaking effective AA implementation should have three methods: bool aa.search( key , &value ); -- const (immutable) method. -- D: key in aa; aa.insert( key, value ); -- mutable method, 1) replaces value under the key if it is there or 2) inserts new key.value pair into the assosiation if does not exist. -- D: aa[key] = value; value aa.get( key, function(key,value) ctor ); -- mutable method, if value is not in collection calls ctor to construct value for the new pair. always returns initialized value. -- ??? This is canonically full set of operations. In presence of default initializers for types in D last 'get' method could be substituted by x = aa[key] with silent construction of new value if needed. Not so convenient but reasonable compromise. Andrew.While that's a good minimal set, I don't think it's an optimal set. An optimal set should have two more: 1) aa.create( key, value ); -- immutable method, 1) the key must not exist. Inserts new key.value pair into the association. Or fails (either returning null or throwing an error.2) value aa.get( key); -- immutable method, fails if value is not in collection.this is what exactly 'search' (in) does but without throwing exception.Although this is a good set of operators, the syntax isn't pretty. One characteristic that a good syntax would have is that it would always be possible to guarantee that the object could be read without being changed. For that reason the expression, e.g.: if (aa["item"]) should be guaranteed to NOT insert "item" into aa.This will be naturally solvable if it would be possible to declare read-only arrays.Syntax is always tricky, and a clear syntax is difficult.
Jul 12 2005
In article <db0p66$vfj$1 digitaldaemon.com>, David Medlock says...I have to say the AA changes are confusing to say the least ...The current semantics helps coders guard against mistakes. Rather than assuming that an AA entry exists, it forces coders to take explicit responsibility for that. As such, your angst could be reduced by acknowledging this paradigm and working with it. In your example, all you need to do to soften the compiler's stance is create a function such as ... and then prior to you accessing a potentially missing entry ... However, I can see that there have been good solid arguments for both types of AA behavior. In some circumstances an AA ought to raise an error if one attempts to access a non-existant entry and in other circumstances an AA ought to create a default entry. As Mr. Bright has experience in doing both for D, it would not be a big stretch for a small syntax change so that one could identify, at AA declaration time, which of the two behaviors the AA will perform. I would recommend that a new token be introduced to mark those AA variables that automatically create a default entry when a non-existing entry is accessed. For indicicative purposes ... S[int] auto lookup; // Declare an auto-create AA S[int] masterlist; // Declare a non-auto-create AA
Jul 12 2005