April 17, 2018
On Tuesday, 17 April 2018 at 21:40:55 UTC, Giles Bathgate wrote:
> Rust calls its version of this function `or_insert_with` (blegh)

Of course, a rustic API could be built atop this PR:

template entry(K, V)
{
    static struct Entry
    {
        alias get this;
        V[K] aa;
        K key;
        V get()
        {
            return aa[key];
        }
        V orInsert()
        {
            return aa.getOrAdd(key);
        }
        V orInsertWith(lazy V value = V.init)
        {
            return aa.getOrAdd(key, value);
        }
    }

    Entry entry(ref V[K] aa, K key)
    {
        return Entry(aa,key);
    }
}


void main()
{
    class C{}
    C[string] aa;
    auto v1 = aa.entry("foo");
    auto v2 = aa.entry("bar").orInsert();
    auto v3 = aa.entry("baz").orInsertWith(new C);
}

But I think, this would be out of scope.


April 18, 2018
On Tuesday, 17 April 2018 at 20:49:30 UTC, Steven Schveighoffer wrote:
> Why do you think it's less efficient to use a lazy parameter?

Wouldn't an extra function call have to happen, at least in some cases?

>> This pattern needs a pointer to be returned, instead of using `ref`. Note that `&inserted` is valid in @safe code, but only with -dip1000. I called the function `slot` because it always returns the address of the slot which the value is stored in.
> Returning ref makes more sense to me -- you are never going to return null.

How do you implement this if the function returns with ref:

bool inserted;
auto p = aa.slot("key", &inserted);
if (inserted) {
  ...
  // set *p
}
else {
  // read *p
  ...
  // set *p
}

There is a common basic use case for this - counting the occurrence of a key in a data set. If the key doesn't exist, initialize and insert the value. *Iff* it does exist, increment the value - I don't think you can do this without functional contortions with your ref return.

(A side benefit is that returning a pointer is consistent with `in`.)
April 18, 2018
On Wednesday, 18 April 2018 at 09:41:48 UTC, Nick Treleaven wrote:
> How do you implement this if the function returns with ref:

I understand where you are coming from, but I am not sure it is appropriate to shoehorn every use case into one api. I think actually what you are describing here is the AddOrUpdate style method https://msdn.microsoft.com/en-us/library/ee378665(v=vs.110).aspx

Perhaps the implementation could be:

    void createOrUpdate(K, V)(ref V[K] aa, K key, V delegate() create, void delegate(V*) update);


    C newc;
    aa.createOrUpdate("key", {
        /* set *p */
        newc = new C;
        return newc;
    },
    (C* u){
        // read *p
        newc = *u;
        // set *p
        *u = new C;
    });
    assert(aa["key"] == newc);

¯\_(ツ)_/¯
April 18, 2018
On 04/18/2018 11:41 AM, Nick Treleaven wrote:
> How do you implement this if the function returns with ref:
> 
> bool inserted;
> auto p = aa.slot("key", &inserted);
> if (inserted) {
>    ...
>    // set *p
> }
> else {
>    // read *p
>    ...
>    // set *p
> }

You can get a pointer from the ref return:

    Value* p = &aa.slot("key", { inserted = true; return Value.init; });

Then do with the pointer whatever you want.
April 18, 2018
On Sunday, 15 April 2018 at 22:55:41 UTC, Giles Bathgate wrote:
> Time for a bikeshed discussion...

I have had some thoughts about the name and would like to share my idea.

Firstly to summarise here are the names that have been found/discussed so far:

C#                   : GetOrAdd
Java                 : computeIfAbsent
Python               : setdefault
Rust                 : entry(key).or_insert_with

Jordan Wilson        : getOrAdd, getOrSet
User1234             : valueOrDefault
Nicholas Wilson      : getOrInsert
Steven Schveighoffer : getPtr, getRef, getInitialized
Nick Treleaven       : slot
MrSmith              : getOrCreate

My latest idea is just a working title, but I think I prefer it to my original getOrAdd.

Giles Bathgate       : require

My thinking is that if you `require` there to be a value in the associative array, then you should have the ability to add one iff it doesn't exist. It name also has parallels with the term in other programming languages to require a module, meaning to import it if it hasn't already been loaded.
April 18, 2018
On Wednesday, 18 April 2018 at 17:19:45 UTC, Giles Bathgate wrote:
> On Sunday, 15 April 2018 at 22:55:41 UTC, Giles Bathgate wrote:
>> Time for a bikeshed discussion...
>
> I have had some thoughts about the name and would like to share my idea.
>
> Firstly to summarise here are the names that have been found/discussed so far:
>
> C#                   : GetOrAdd
> Java                 : computeIfAbsent
> Python               : setdefault
> Rust                 : entry(key).or_insert_with
>
> Jordan Wilson        : getOrAdd, getOrSet
> User1234             : valueOrDefault
> Nicholas Wilson      : getOrInsert
> Steven Schveighoffer : getPtr, getRef, getInitialized
> Nick Treleaven       : slot
> MrSmith              : getOrCreate
>
> My latest idea is just a working title, but I think I prefer it to my original getOrAdd.
>
> Giles Bathgate       : require
>
> My thinking is that if you `require` there to be a value in the associative array, then you should have the ability to add one iff it doesn't exist. It name also has parallels with the term in other programming languages to require a module, meaning to import it if it hasn't already been loaded.

Thinking seems sound, although having a name starting with "get" does have the advantage of being more related to the existing get.

Weirdly, in all cases if I replace "Or" with "Else", it seems to read easier for me... getElseAdd, getElseCreate, etc.

Ultimately I'm not too bothered with any name really, I'm just looking forward to using it eventually :-)

Jordan
April 19, 2018
On Wednesday, 18 April 2018 at 21:04:53 UTC, Jordan Wilson wrote:
> Thinking seems sound, although having a name starting with "get" does have the advantage of being more related to the existing get.

Ah yes, good point. I think now we've had the discussion about other use cases though that ultimately there might need to be an "atomic" AddOrUpdate style function too, which I was thinking could just be called 'update'. The necessity to keep them all starting with get seemed less important.
April 19, 2018
On Wednesday, April 18, 2018 17:19:45 Giles Bathgate via Digitalmars-d wrote:
> On Sunday, 15 April 2018 at 22:55:41 UTC, Giles Bathgate wrote:
> > Time for a bikeshed discussion...
>
> I have had some thoughts about the name and would like to share my idea.
>
> Firstly to summarise here are the names that have been found/discussed so far:
>
> C#                   : GetOrAdd
> Java                 : computeIfAbsent
> Python               : setdefault
> Rust                 : entry(key).or_insert_with
>
> Jordan Wilson        : getOrAdd, getOrSet
> User1234             : valueOrDefault
> Nicholas Wilson      : getOrInsert
> Steven Schveighoffer : getPtr, getRef, getInitialized
> Nick Treleaven       : slot
> MrSmith              : getOrCreate
>
> My latest idea is just a working title, but I think I prefer it to my original getOrAdd.
>
> Giles Bathgate       : require
>
> My thinking is that if you `require` there to be a value in the associative array, then you should have the ability to add one iff it doesn't exist. It name also has parallels with the term in other programming languages to require a module, meaning to import it if it hasn't already been loaded.

Out of all of those, I _really_ hope that you don't go with require. It sounds like the sort of thing that you'd get in a library having to do with unit testing or contracts and gives no indication whatsoever as to what it does. The only one in that list that seems similarly opaque is slot. Those names say nothing about either getting a value or adding / inserting one. Every other name at least gives some clue as to what the function does.

If I were adding it, and we already had get, I would just make it an optional argument to get, which wouldn't necessarily be clear about inserting, but it would at least be clear about getting. However, since we used overloaded operators, there's no get.

Another option would be getOrInit, though I agree that they're all kind of ugly.

Either way, IMHO, getOrAdd is infinitely better than require.

- Jonathan M Davis

April 20, 2018
On Wednesday, 18 April 2018 at 16:47:50 UTC, ag0aep6g wrote:
> You can get a pointer from the ref return:
>
>     Value* p = &aa.slot("key", { inserted = true; return Value.init; });

This is not @safe, even with -dip1000:

Error: cannot take address of ref return of f() in @safe function main
April 20, 2018
On Wednesday, 18 April 2018 at 16:04:13 UTC, Giles Bathgate wrote:
> I understand where you are coming from, but I am not sure it is appropriate to shoehorn every use case into one api.

It is not shoehorning, the difference here is just returning by ref vs pointer. I understand that, as the pointer address is not needed by the caller, it is cleaner to use ref. The problem is that local refs are not supported by D, and so I would need to do this (at least in safe code):

import std.functional : unaryFun;
bool ins;
aa.update("key", {ins = true; return 1;}).unaryFun!((v){ if (ins) v++; });

This seems pretty convoluted. It could be made a bit better if D supported UFCS for lambdas, then unaryFun wouldn't be needed.

BTW, I like the name `update`.

>     void createOrUpdate(K, V)(ref V[K] aa, K key, V delegate() create, void delegate(V*) update);

Again, would the delegate calls always be inlined, in all cases?

I think having a low-level API in druntime is appropriate, it's a runtime library after all.