Thread overview
How to update Associative Array?
Feb 10, 2022
tastyminerals
Feb 10, 2022
bauss
Feb 10, 2022
tastyminerals
Feb 10, 2022
novice2
Feb 10, 2022
Ali Çehreli
Feb 12, 2022
kdevel
Feb 13, 2022
Ali Çehreli
Feb 13, 2022
kdevel
Feb 14, 2022
Ali Çehreli
Feb 18, 2022
kdevel
February 10, 2022

Not sure if the update method got changed but I am having trouble with understanding it now.
I assumed it would work as easy as in Python:) Just do mydic.update(dic) or mydic["key"].update(anotherDic).

The docs have the following example.

class C{}
C[string] aa;

C older;
C newer;
aa.update("a",
{
    newer = new C;
    return newer;
},
(ref C c)
{
    older = c;
    newer = new C;
    return newer;
});

This looks pretty scary and confusing to me tbo. Also, why is there an example with class and not simple int[string]? I just need to know how can I update let's say int[string] or nested int[string][string] AA. Should I also initialise and additional C classes before calling this method as in the example?

Considering this is the only example in the docs I could find on how to update AA, imagine someone from Python world comes and sees this. Next thing he does, is close the page and never come back to D again :(

February 10, 2022

On Thursday, 10 February 2022 at 10:59:17 UTC, tastyminerals wrote:

>

Not sure if the update method got changed but I am having trouble with understanding it now.
I assumed it would work as easy as in Python:) Just do mydic.update(dic) or mydic["key"].update(anotherDic).

The docs have the following example.

class C{}
C[string] aa;

C older;
C newer;
aa.update("a",
{
    newer = new C;
    return newer;
},
(ref C c)
{
    older = c;
    newer = new C;
    return newer;
});

This looks pretty scary and confusing to me tbo. Also, why is there an example with class and not simple int[string]? I just need to know how can I update let's say int[string] or nested int[string][string] AA. Should I also initialise and additional C classes before calling this method as in the example?

Considering this is the only example in the docs I could find on how to update AA, imagine someone from Python world comes and sees this. Next thing he does, is close the page and never come back to D again :(

You can just do:

aa[key] = value.

If the key exist then it's updated, if it doesn't then it's added.

The reason for the update function is simply in cases where you want to use the oldvalue etc. perhaps it has a timestamp that you need to keep etc.

But in general you shouldn't need it.

February 10, 2022

On Thursday, 10 February 2022 at 12:04:04 UTC, bauss wrote:

>

On Thursday, 10 February 2022 at 10:59:17 UTC, tastyminerals wrote:

>

[...]

You can just do:

aa[key] = value.

If the key exist then it's updated, if it doesn't then it's added.

The reason for the update function is simply in cases where you want to use the oldvalue etc. perhaps it has a timestamp that you need to keep etc.

But in general you shouldn't need it.

I meant a different thing though. I am looking for mydic.update(another_dic) analogue where {"a": 1, "b": 2} update {"c": 3, "a": -1} becomes {"a":-1, "b": 2, "c": 3}.

February 10, 2022

On Thursday, 10 February 2022 at 12:08:07 UTC, tastyminerals wrote:

>

I meant a different thing though. I am looking for mydic.update(another_dic) analogue where {"a": 1, "b": 2} update {"c": 3, "a": -1} becomes {"a":-1, "b": 2, "c": 3}.

you need "merge"?
https://forum.dlang.org/post/fhhuupczjnhehxpljxrj@forum.dlang.org

February 10, 2022
On 2/10/22 05:42, novice2 wrote:
> On Thursday, 10 February 2022 at 12:08:07 UTC, tastyminerals wrote:
>> I meant a different thing though. I am looking for
>> `mydic.update(another_dic)` analogue where `{"a": 1, "b": 2}` update
>> `{"c": 3, "a": -1}` becomes `{"a":-1, "b": 2, "c": 3}`.
>
> you need "merge"?
> https://forum.dlang.org/post/fhhuupczjnhehxpljxrj@forum.dlang.org
>

Probably but Python's update updates the original dictionary. I came up with the following function:

import std.traits : isAssociativeArray, isImplicitlyConvertible, KeyType, ValueType;

void update(Target, From)(ref Target target, From from)
if (isAssociativeArray!Target &&
    isAssociativeArray!From &&
    isImplicitlyConvertible!(KeyType!From, KeyType!Target) &&
    isImplicitlyConvertible!(ValueType!From, ValueType!Target))
{
  foreach (kv; from.byKeyValue) {
    target[kv.key] = kv.value;
  }
}

unittest {
  auto a = [ "a": 1, "b": 2 ];
  a.update([ "c": 3, "a": -1 ]);
  assert(a == [ "a": -1, "b": 2, "c": 3 ]);
}

void main() {
}

Yes, it may look scary to newcomers to D but the template constraint is just for improved usability. (Still, I suspect it's not complete.) Otherwise, the following is the same thing:

void update(Target, From)(ref Target target, From from) {
  foreach (kv; from.byKeyValue) {
    target[kv.key] = kv.value;
  }
}

'ref' is needed there to work with null AAs. Without 'ref', the following would not work:

  int[string] aa;
  aa.update([ "x" : 42 ]);

'aa' would be null! Welcome to AA reference semantics. :)

Ali

February 12, 2022
On Thursday, 10 February 2022 at 17:09:23 UTC, Ali Çehreli wrote:
> import std.traits : isAssociativeArray, isImplicitlyConvertible, KeyType, ValueType;
>
> void update(Target, From)(ref Target target, From from)
> if (isAssociativeArray!Target &&
>     isAssociativeArray!From &&
>     isImplicitlyConvertible!(KeyType!From, KeyType!Target) &&
>     isImplicitlyConvertible!(ValueType!From, ValueType!Target))
> {
>   foreach (kv; from.byKeyValue) {
>     target[kv.key] = kv.value;
>   }
> }

[...]

> Yes, it may look scary to newcomers to D but the template constraint is just for improved usability.

It also looks scary to me and I use D now for quite a while. Assume I have this client code:

   string[int] q;
   byte[short] r;
   q.update (r);

It produces this error message from your version:

   $ dmd -checkaction=context -unittest -run v1
   v1.d(21): Error: template `v1.update` cannot deduce function from argument types `!()(string[int], byte[short])`
   v1.d(3):        Candidate is: `update(Target, From)(ref Target target, From from)`
     with `Target = string[int],
          From = byte[short]`
     must satisfy the following constraint:
   `       isImplicitlyConvertible!(ValueType!From, ValueType!Target)`

If I remove the constraint (the if-stuff) I get

   v1.d(12): Error: cannot implicitly convert expression `kv.value()` of type `byte` to `string`
   v1.d(23): Error: template instance `v1.update!(string[int], byte[short])` error instantiating

Can this really be improved?

Stefan
February 12, 2022
On 2/12/22 13:37, kdevel wrote:
> On Thursday, 10 February 2022 at 17:09:23 UTC, Ali Çehreli wrote:
>> import std.traits : isAssociativeArray, isImplicitlyConvertible,
>> KeyType, ValueType;
>>
>> void update(Target, From)(ref Target target, From from)
>> if (isAssociativeArray!Target &&
>>     isAssociativeArray!From &&
>>     isImplicitlyConvertible!(KeyType!From, KeyType!Target) &&
>>     isImplicitlyConvertible!(ValueType!From, ValueType!Target))
>> {
>>   foreach (kv; from.byKeyValue) {
>>     target[kv.key] = kv.value;
>>   }
>> }
>
> [...]
>
>> Yes, it may look scary to newcomers to D but the template constraint
>> is just for improved usability.
>
> It also looks scary to me and I use D now for quite a while. Assume I
> have this client code:
>
>     string[int] q;
>     byte[short] r;
>     q.update (r);

So, that code is not valid because a 'byte' cannot implicitly be converted to a 'string'.

> It produces this error message from your version:
>
>     $ dmd -checkaction=context -unittest -run v1
>     v1.d(21): Error: template `v1.update` cannot deduce function from
> argument types `!()(string[int], byte[short])`
>     v1.d(3):        Candidate is: `update(Target, From)(ref Target
> target, From from)`
>       with `Target = string[int],
>            From = byte[short]`
>       must satisfy the following constraint:
>     `       isImplicitlyConvertible!(ValueType!From, ValueType!Target)`

That looks like a wall of text but is much better than before. Now, the compiler is telling us what requirement could not be met.

Additionally, the compilation fail in user code; telling the user why their code is not valid.

> If I remove the constraint (the if-stuff) I get
>
>     v1.d(12): Error: cannot implicitly convert expression `kv.value()`
> of type `byte` to `string`
>     v1.d(23): Error: template instance `v1.update!(string[int],
> byte[short])` error instantiating

That's inferior because now the error message is pointing at the implementation of a library function and confuses everyone. We have such functions in Phobos.

> Can this really be improved?

I am not sure.

Here is an idea, which doesn't seem to add much as is:

'if' constraints could have a message for an improved error message like "This function template could not be used because 'byte' cannot implicitly be converted to 'string'." Note that there could be many candidates like "And this function template could not be used because 42 is not prime", etc. The programmer would read all those and figure out usable parameters.

But I think those would just be translations of the error messages.

Another idea is perhaps the compiler could resolve ValueType!From to 'byte' and provide the more readable

  isImplicitlyConvertible!(byte, string)

I don't know how feasible that is.

Still, I am glad the error messages today are much better. After all, everything that the programmer needs is spelled out here. It requires following some types:


      with `Target = string[int],
           From = byte[short]`
      must satisfy the following constraint:
             isImplicitlyConvertible!(ValueType!From, ValueType!Target)`

The only thing that's not readily available there is ValueType but at least it's very readable.

Sorry, I rambled but I'm under the impression that this complexity is inherent.

Ali

February 13, 2022
On Sunday, 13 February 2022 at 01:27:45 UTC, Ali Çehreli wrote:
> [...]
> > If I remove the constraint (the if-stuff) I get
> >
> >     v1.d(12): Error: cannot implicitly convert expression
> `kv.value()`
> > of type `byte` to `string`
> >     v1.d(23): Error: template instance
> `v1.update!(string[int],
> > byte[short])` error instantiating
>
> That's inferior because now the error message is pointing at the implementation of a library function and confuses everyone.

It confuses me not really. (Function) templates are by their very nature
visible to the user and I got used to reading the implementations.
Nonetheless may the compiler-generated checks be moved into the interface:

   void update(Target, From)(ref Target target, From from, Target dummy = From.init)

For this function template dmd reports in case of incompatible types:

   cannot implicitly convert expression `null` of type `byte[short]` to `string[int]`

> [...]

> After all, everything that the programmer needs is spelled out here. It requires following some types:
>
>
>       with `Target = string[int],
>            From = byte[short]`
>       must satisfy the following constraint:
>              isImplicitlyConvertible!(ValueType!From, ValueType!Target)`
>
> The only thing that's not readily available there is ValueType but at least it's very readable.

The constraints appear artificial to me: Neither are there means which ensure
that a list of Constraints is complete nor is it ensured that a list of
constraints is not overly restrictive. It seems that these Constraints constitute
another class of things which may get out of sync with the actual code (besides program documentation, program comments and function names).

Doesn't this re-iteration of what is already implicitly contained in the line

   target[kv.key] = kv.value;

violate the DRY principle?


February 13, 2022
On 2/13/22 05:58, kdevel wrote:
> On Sunday, 13 February 2022 at 01:27:45 UTC, Ali Çehreli wrote:
>> [...]
>> > If I remove the constraint (the if-stuff) I get
>> >
>> >     v1.d(12): Error: cannot implicitly convert expression
>> `kv.value()`
>> > of type `byte` to `string`
>> >     v1.d(23): Error: template instance
>> `v1.update!(string[int],
>> > byte[short])` error instantiating
>>
>> That's inferior because now the error message is pointing at the
>> implementation of a library function and confuses everyone.
>
> It confuses me not really. (Function) templates are by their very nature
> visible to the user and I got used to reading the implementations.

Point taken but how deep should we understand library code, not all being Phobos. My pet peeve:

import std.stdio;

void main(string[] args) {
  writefln!"hello"(42);
}

/usr/include/dlang/dmd/std/stdio.d(4442): Error: no property `msg` for type `string`

I am happy that it's caught at compile time but what 'msg' property are we talking about? What does it have to be with me?

I would like it if the

It would be better if the compilation error said:

  Must satify formatSpecsMatchArgs!("hello", int)

I am not sure whether it's practical or efficient at compile time.

> The constraints appear artificial to me: Neither are there means which
> ensure
> that a list of Constraints is complete nor is it ensured that a list of
> constraints is not overly restrictive.

Agreed.

> It seems that these Constraints
> constitute
> another class of things which may get out of sync with the actual code
> (besides program documentation, program comments and function names).

Agreed.

>
> Doesn't this re-iteration of what is already implicitly contained in the
> line
>
>     target[kv.key] = kv.value;

I don't agree with that because there is no 'target', 'kv', etc. in the programmer's code. Should we really expect the programmers be programmers? :p

> violate the DRY principle?

Agreed.

Ali

February 18, 2022
On Monday, 14 February 2022 at 01:04:08 UTC, Ali Çehreli wrote:

[...]

> Point taken but how deep should we understand library code, not all being Phobos. My pet peeve:
>
> import std.stdio;
>
> void main(string[] args) {
>   writefln!"hello"(42);
> }
>
> /usr/include/dlang/dmd/std/stdio.d(4442): Error: no property `msg` for type `string`
>
> I am happy that it's caught at compile time but what 'msg' property are we talking about? What does it have to be with me?
>
> I would like it if the
>
> It would be better if the compilation error said:
>
>   Must satify formatSpecsMatchArgs!("hello", int)

Interestingly

   writefln ("hello", 42);

does not throw at runtime.