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

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

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


February 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...
>> 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
>
>> 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?
>>
> This is what it does.

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.

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

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

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

February 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 { }
February 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.
February 18, 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
February 18, 2012
On 18/02/2012 21:42, Ben Davis wrote:
> 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.

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?
February 18, 2012
Are you familiar with cases where you want regular arrays to return Type.init when you go out of bounds?