View mode: basic / threaded / horizontal-split · Log in · Help
February 18, 2012
Re: When are associative arrays meant to throw a RangeError?
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
Re: When are associative arrays meant to throw a RangeError?
"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
Re: When are associative arrays meant to throw a RangeError?
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
Re: When are associative arrays meant to throw a RangeError?
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
Re: When are associative arrays meant to throw a RangeError?
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
Re: When are associative arrays meant to throw a RangeError?
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
Re: When are associative arrays meant to throw a RangeError?
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
Re: When are associative arrays meant to throw a RangeError?
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
Re: When are associative arrays meant to throw a RangeError?
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
Re: When are associative arrays meant to throw a RangeError?
Are you familiar with cases where you want regular arrays to return
Type.init when you go out of bounds?
1 2 3 4
Top | Discussion index | About this forum | D home