July 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...
>>>>> * consistent with dynamic and static arrays where requesting a value not in the array throws
>>>> Throwing exceptions are supposed to be for unusual circumstances, not normal execution.  I would hope D isn't moving towards checked Exceptions.
>>>
>>> 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.
>>
>> 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. :-P
>
>:) 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.
>
>


July 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
July 13, 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.
July 13, 2005
"Charles Hixson" <charleshixsn@earthlink.net> wrote in message news:db1v3l$1v8v$1@digitaldaemon.com...
> 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.

Yep. Nice to have but is quite rare I guess.
PS: it is definitely mutable.

> 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.


July 13, 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 ...

#  void autoexist(inout S[int] x, int y)
#  {
#      if (y in x == null) x[y] = *new S;
#  }

and then prior to you accessing a potentially missing entry ...

#   lookup.autoexist(key);

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



July 13, 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:
# aa[key] = value;

-- Chris Sauls

PS: Sample test program.
# import std.stdio;
#
# int main (in char[][] args) {
#   int[char[]] map;
#
#   // map.Add("foo"); // <-- This does not compile.
#   map["bar"] = 256;
#   foreach (char[] key, int value; map)
#     writefln("%s: %s", key, value);
#   return 0;
# }
July 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)
>* more like Java, C#, Ruby (which return null and only work for classes) and
>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

I, 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


July 13, 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
July 13, 2005
On Wed, 13 Jul 2005 11:26:43 +0200, Anders F Björklund <afb@algonet.se> wrote:
> 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;

Or, using one lookup:

try { c = aa[100]; }
catch(ArrayBoundsError e) { c = new MyClass(); }

Regan
July 13, 2005
Regan Heath wrote:

>> 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;
> 
> Or, using one lookup:
> 
> try { c = aa[100]; }
> catch(ArrayBoundsError e) { c = new MyClass(); }

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 :-)

--anders