July 12, 2005
Ben Hinkle wrote:

> "David Medlock" <noone@nowhere.com> wrote in message 
>>
>>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...

Since you see a benefit, please tell me what it is.  It still escapes me.

-DavidM

July 12, 2005
"David Medlock" <noone@nowhere.com> wrote in message news:db11io$174u$1@digitaldaemon.com...
> Ben Hinkle wrote:
>
>> "David Medlock" <noone@nowhere.com> wrote in message
>>>
>>>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...
>
> Since you see a benefit, please tell me what it is.  It still escapes me.
>
> -DavidM
>

* 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


July 12, 2005
In article <db118h$16qm$1@digitaldaemon.com>, Ben Hinkle says...
>
>>>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.
>
>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.
>

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.


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





July 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...
>>
>>>>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.
>>
>>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.
>>
>
> Hrm.. It didn't say that in the changelog, but i'll believe you. (See the
> changelog for DMD 0.126)

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.

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


July 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!

> * more like Java, C#, Ruby (which return null and only work for classes) and Python (which errors)
As you say, Java, C# and Ruby don't have a builtin and do not work with 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-safe
Thread 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 throws 
Throwing 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
July 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.
such AA# has method x = aa[key] implemented differently -
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.




July 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!
>
>> * more like Java, C#, Ruby (which return null and only work for classes) and Python (which errors)
> As you say, Java, C# and Ruby don't have a builtin and do not work with 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-safe
> Thread 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.

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

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


July 12, 2005
>>> * 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


July 12, 2005
"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.