digitalmars.D - Feature to get or add value to an associative array.
- Giles Bathgate (35/35) Apr 15 2018 Hi,
- Giles Bathgate (4/5) Apr 15 2018 I posted details of the PR here because at least 2 people do not
- Jordan Wilson (5/11) Apr 15 2018 I think Adding a Yes.add flag to the existing get makes sense,
- Giles Bathgate (4/5) Apr 16 2018 That's an interesting idea. I forgot to mention that getOrAdd is
- bauss (5/18) Apr 16 2018 I'm opposed to flags. I hate the whole "yes" "no" thing when it
- user1234 (2/8) Apr 16 2018 It's hard to name... I cant find better than "valueOrDefault"
- Giles Bathgate (23/24) Apr 18 2018 I have had some thoughts about the name and would like to share
- Jordan Wilson (8/32) Apr 18 2018 Thinking seems sound, although having a name starting with "get"
- Giles Bathgate (6/9) Apr 19 2018 Ah yes, good point. I think now we've had the discussion about
- Dominikus Dittes Scherkl (3/13) Apr 20 2018 How about something like "forceGet" to make clear you will get
- Jonathan M Davis (16/40) Apr 19 2018 Out of all of those, I _really_ hope that you don't go with require. It
- Giles Bathgate (8/12) Apr 20 2018 Yes I know. I gave up on trying to find a single verb or
- JN (8/12) Apr 16 2018 I am not sure if it's a good idea to combine accessor and
- Giles Bathgate (8/12) Apr 16 2018 You can still use a combination of `in` and the update syntax if
- Cym13 (10/22) Apr 16 2018 "in" returns a pointer to the object, there'es not double lookup
- Nicholas Wilson (3/7) Apr 16 2018 I think `getOrInsert` is a good name for this function
- Giles Bathgate (8/16) Apr 17 2018 This doesn't work. `in` returns null when the key doesn't exist.
- Giles Bathgate (3/5) Apr 17 2018 The java name for such a function is `computeIfAbsent`
- Giles Bathgate (5/6) Apr 17 2018 More names from other languages, this time python:
- Steven Schveighoffer (18/24) Apr 17 2018 I think this is a great addition. I've always disliked the double-lookup...
- Nick Treleaven (18/23) Apr 17 2018 Thanks for making this pull, I've thought about solving this
- Giles Bathgate (6/8) Apr 17 2018 I like the name. I think your version is quite low level which
- MrSmith (8/13) Apr 17 2018 You may need to perform extra logic when value is inserted in the
- Giles Bathgate (3/5) Apr 17 2018 Fair enough, I will consider adding this and some tests to the
- Steven Schveighoffer (11/29) Apr 17 2018 Not as straightforward, but it can be done:
- Giles Bathgate (9/13) Apr 17 2018 Yes, I like that approach. I don't want to bloat the feature at
- Giles Bathgate (36/37) Apr 17 2018 Of course, a rustic API could be built atop this PR:
- Nick Treleaven (23/30) Apr 18 2018 Wouldn't an extra function call have to happen, at least in some
- Giles Bathgate (23/24) Apr 18 2018 I understand where you are coming from, but I am not sure it is
- Nick Treleaven (16/20) Apr 20 2018 It is not shoehorning, the difference here is just returning by
- Giles Bathgate (24/27) Apr 20 2018 So the low-level API you are requesting is part of the pull
- ag0aep6g (4/17) Apr 18 2018 You can get a pointer from the ref return:
- Nick Treleaven (4/7) Apr 20 2018 This is not @safe, even with -dip1000:
- ag0aep6g (5/13) Apr 20 2018 Hm. Do you know why that's not allowed? I can't see how it would be less...
- Jonathan M Davis (9/23) Apr 20 2018 The compiler assumes that a pointer is valid when determining whether co...
- ag0aep6g (11/17) Apr 20 2018 Can't it do the same for ref returns? Unsafe stuff like returning
- Steven Schveighoffer (15/23) Apr 20 2018 Hm... I would have expected it to work in dip1000.
- Uknown (5/30) Apr 20 2018 The error seems to be about type mismatching, it works if you use
- Steven Schveighoffer (5/39) Apr 20 2018 Sorry, it was a typo. In my real code I have:
- Uknown (5/15) Apr 20 2018 It seems like a bug. Changing foo to take and return an `int *`
- Steven Schveighoffer (7/16) Apr 20 2018 Let me say I was surprised that this doesn't actually work. An in-line
- Jonathan M Davis (17/32) Apr 20 2018 I'm not sure. I mucked around with lazy like this when I was originally
- Steven Schveighoffer (16/52) Apr 20 2018 But, it *is* a delegate. literally, that's what gets implemented (and it...
- Jonathan M Davis (56/111) Apr 20 2018 If we want to make the compiler accept it, then I'm not necessarily agai...
- Giles Bathgate (7/11) Apr 20 2018 I kind of expected it to work too. However, I just created a
- Jonathan M Davis (10/22) Apr 20 2018 Honestly, I think that it's a terrible idea to special-case it like that...
- Giles Bathgate (7/16) Apr 20 2018 Again lack of experience, so I presume you can just do:
- Jonathan M Davis (13/29) Apr 20 2018 Yes. That should work. e.g. this
- Giles Bathgate (3/15) Apr 20 2018 Ok cool. I've updated the PR since basically, I agree with your
- Steven Schveighoffer (5/26) Apr 20 2018 Yeah, I didn't mean for the special case to happen -- I thought it just
- Steven Schveighoffer (14/32) Apr 20 2018 The drawback here, of course, is that it's a lambda calling a lambda (if...
- Jonathan M Davis (6/36) Apr 20 2018 Honestly, I would have considered it a bug that it accepts 1, since that...
- Andrea Fontana (18/22) Apr 24 2018 Yes please :)
- Giles Bathgate (4/5) Apr 25 2018 The intention is that it will work for value types. I will add
Hi, I wanted a way to lazily insert a value into an associative array, and I am proposing a new function called getOrAdd in https://github.com/dlang/druntime/pull/2162 The function provides a means to get a value corresponding to the key, but if the value doesn't exist it will evaluate the lazy argument to create a new value, add this to the associative array and then return it. ``` auto p = lookup.getOrAdd("giles", new Person); p.eyeColor = Color.Brown; //... assert("giles" in lookup); ``` Corresponding documentation added in https://github.com/dlang/dlang.org/pull/2343 Implementation details: Traditionally the above example could be done like so: ``` auto p = "giles" in lookup; if (p is null) { p = new Person; lookup["giles"] = p; } p.eyeColor = Color.Brown; //... assert("giles" in lookup); ``` I find this later code clunky and it requires two hashes/lookups. The proposed implementation adds support directly to rt/aaA.d to avoid this. I should also point out that the allocation of a new Person in the example is trivial, but it might often be the case that the person instance is created by fetching a record from a db, or an Image loaded from disk, or a movie downloaded from the internet.
Apr 15 2018
On Sunday, 15 April 2018 at 22:52:47 UTC, Giles Bathgate wrote:I am proposing a new function called getOrAdd inI posted details of the PR here because at least 2 people do not like the name getOrAdd (and one of those people being myself) Time for a bikeshed discussion...
Apr 15 2018
On Sunday, 15 April 2018 at 22:55:41 UTC, Giles Bathgate wrote:On Sunday, 15 April 2018 at 22:52:47 UTC, Giles Bathgate wrote:I think Adding a Yes.add flag to the existing get makes sense, but property functions only have max 2 args. Soooo...getOrAdd or getOrSet would be my pick. JordanI am proposing a new function called getOrAdd inI posted details of the PR here because at least 2 people do not like the name getOrAdd (and one of those people being myself) Time for a bikeshed discussion...
Apr 15 2018
On Monday, 16 April 2018 at 03:42:18 UTC, Jordan Wilson wrote:I think Adding a Yes.add flag to the existing get makes senseThat's an interesting idea. I forgot to mention that getOrAdd is a mutable function whereas get is inout, so that might also be problematic.
Apr 16 2018
On Monday, 16 April 2018 at 03:42:18 UTC, Jordan Wilson wrote:On Sunday, 15 April 2018 at 22:55:41 UTC, Giles Bathgate wrote:I'm opposed to flags. I hate the whole "yes" "no" thing when it comes to such things, just like the "true" "false"... It's a no go for me. A function name that fits would be perfect in my opinion.On Sunday, 15 April 2018 at 22:52:47 UTC, Giles Bathgate wrote:I think Adding a Yes.add flag to the existing get makes sense, but property functions only have max 2 args. Soooo...getOrAdd or getOrSet would be my pick. JordanI am proposing a new function called getOrAdd inI posted details of the PR here because at least 2 people do not like the name getOrAdd (and one of those people being myself) Time for a bikeshed discussion...
Apr 16 2018
On Sunday, 15 April 2018 at 22:55:41 UTC, Giles Bathgate wrote:On Sunday, 15 April 2018 at 22:52:47 UTC, Giles Bathgate wrote:It's hard to name... I cant find better than "valueOrDefault"I am proposing a new function called getOrAdd inI posted details of the PR here because at least 2 people do not like the name getOrAdd (and one of those people being myself) Time for a bikeshed discussion...
Apr 16 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: 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.
Apr 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: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 :-) JordanTime 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: 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.
Apr 18 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.
Apr 19 2018
On Thursday, 19 April 2018 at 08:20:02 UTC, Giles Bathgate wrote:On Wednesday, 18 April 2018 at 21:04:53 UTC, Jordan Wilson wrote:How about something like "forceGet" to make clear you will get it, even if it wasn't there before.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.
Apr 20 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: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 DavisTime 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: 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.
Apr 19 2018
On Friday, 20 April 2018 at 02:12:37 UTC, Jonathan M Davis wrote: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.Yes I know. I gave up on trying to find a single verb or runTogetherWords that would describe exactly what it does, and instead opted for something that I like, and added documentation to describe what it does. I don't think I will find a name that everybody likes. But I am open to more suggestions as its probably the easiest aspect of the PR to change.
Apr 20 2018
On Sunday, 15 April 2018 at 22:52:47 UTC, Giles Bathgate wrote:Hi, I wanted a way to lazily insert a value into an associative array, and I am proposing a new function called getOrAdd in https://github.com/dlang/druntime/pull/2162I am not sure if it's a good idea to combine accessor and insertion in a same method call. Is it really such a big overhead to do "if in assocarray then take else create new"? It's only one additional "in", but makes the code more explicit and clear. I think in most cases, you will want to check if you are dealing with a fetched object or a default created one, so that will complicate the function even further.
Apr 16 2018
On Monday, 16 April 2018 at 12:41:07 UTC, JN wrote:It's only one additional "in", but makes the code more explicit and clear. I think in most cases, you will want to check if you are dealing with a fetched object or a default created one, so that will complicate the function even further.You can still use a combination of `in` and the update syntax if you want, this doesn't take that away. In the future, for a concurrent implementation of associative arrays, a way of getting or adding an element as an atomic operation becomes more important. Either way the beauty of hashed based lookups is that they average O(1), seems a shame to double that up for no reason ;)
Apr 16 2018
On Monday, 16 April 2018 at 18:59:54 UTC, Giles Bathgate wrote:On Monday, 16 April 2018 at 12:41:07 UTC, JN wrote:"in" returns a pointer to the object, there'es not double lookup necessary: // if we don't know .get(key, default) exists auto ptr = key in aa; auto value = ptr ? *ptr : default; // to set default value on the fly auto value = ptr ? *ptr : *ptr = default; is a new flag/method really that necessary? In my experience if you have trouble naming it you haven't found its true purpose yet.It's only one additional "in", but makes the code more explicit and clear. I think in most cases, you will want to check if you are dealing with a fetched object or a default created one, so that will complicate the function even further.You can still use a combination of `in` and the update syntax if you want, this doesn't take that away. In the future, for a concurrent implementation of associative arrays, a way of getting or adding an element as an atomic operation becomes more important. Either way the beauty of hashed based lookups is that they average O(1), seems a shame to double that up for no reason ;)
Apr 16 2018
On Tuesday, 17 April 2018 at 00:04:32 UTC, Cym13 wrote:auto value = ptr ? *ptr : *ptr = default;That is going to crash if ptr is null.is a new flag/method really that necessary? In my experience if you have trouble naming it you haven't found its true purpose yet.I think `getOrInsert` is a good name for this function
Apr 16 2018
On Tuesday, 17 April 2018 at 00:04:32 UTC, Cym13 wrote:"in" returns a pointer to the object, there'es not double lookup necessary: // if we don't know .get(key, default) exists auto ptr = key in aa; auto value = ptr ? *ptr : default;This doesn't work. `in` returns null when the key doesn't exist. So you are de-referencing null: https://github.com/dlang/druntime/blob/master/src/rt/aaA.d#L417is a new flag/method really that necessary? In my experience if you have trouble naming it you haven't found its true purpose yet.My personal reason for not liking it is because the same name is used by Microsoft: https://msdn.microsoft.com/en-us/library/ee378676(v=vs.110).aspx I think I have clearly explained its purpose.
Apr 17 2018
On Tuesday, 17 April 2018 at 07:40:23 UTC, Giles Bathgate wrote:My personal reason for not liking it is because the same name is used by MicrosoftThe java name for such a function is `computeIfAbsent` https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#computeIfAbsent-K-java.util.function.Function-
Apr 17 2018
On Tuesday, 17 April 2018 at 09:21:14 UTC, Giles Bathgate wrote:The java name for such a function is `computeIfAbsent`More names from other languages, this time python: https://docs.python.org/3/library/stdtypes.html#dict.setdefault It's horrid though, a method called `set` that returns a value? Maybe I am biased against anything pythonic ;)
Apr 17 2018
On 4/15/18 6:52 PM, Giles Bathgate wrote:I find this later code clunky and it requires two hashes/lookups. The proposed implementation adds support directly to rt/aaA.d to avoid this. I should also point out that the allocation of a new Person in the example is trivial, but it might often be the case that the person instance is created by fetching a record from a db, or an Image loaded from disk, or a movie downloaded from the internet.I think this is a great addition. I've always disliked the double-lookup requirement for ensuring a key was initialized. I'll note that C++ gets around this by always initializing on any access. The name "getOrAdd" is a bit mechanical sounding. The real reason you want this is to ensure that key's value is initialized. ensureInitialized is long. get is already taken (if get weren't already taken, I'd suggest that name). Others have suggested using flags, but I'll note to them, `Flag` and `Yes` are part of std.typecons, and not accessible here. getWithDefault sounds reasonable but probably would be confusing with the current `get` overload. Maybe getInitialized? I don't know that either of these are giant leaps ahead of getOrAdd. Another possibility is getPtr. get currently gets a value, or returns a default value if it doesn't exist. But getPtr would return a pointer to the value, ensuring it's initialized. In any case, thumbs up to the concept, and I'm ambivalent on the name. -Steve
Apr 17 2018
On Sunday, 15 April 2018 at 22:52:47 UTC, Giles Bathgate wrote:The function provides a means to get a value corresponding to the key, but if the value doesn't exist it will evaluate the lazy argument to create a new value, add this to the associative array and then return it. auto p = lookup.getOrAdd("giles", new Person);Thanks for making this pull, I've thought about solving this before. I think the function needs to provide a way to tell if the value was already present. I also think it's more ergonomic not to have to use a lazy argument, and probably more efficient, so I had in mind: Value* slot(AA aa, K key, scope bool* inserted = null); bool inserted; auto p = aa.slot("key", &inserted); if (inserted) { ... *p = new Value(...); } // use *p 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.
Apr 17 2018
On Tuesday, 17 April 2018 at 16:18:32 UTC, Nick Treleaven wrote:I called the function `slot` because it always returns the address of the slot which the value is stored in.I like the name. I think your version is quite low level which ultimately provides more power at the expense of making the callee code less clean. I am not sure with D which of those two aspects is preferred. Perhaps both functions could be provided? What is the use case for knowing whether a value was inserted.
Apr 17 2018
On Tuesday, 17 April 2018 at 17:27:02 UTC, Giles Bathgate wrote:I like the name. I think your version is quite low level which ultimately provides more power at the expense of making the callee code less clean. I am not sure with D which of those two aspects is preferred. Perhaps both functions could be provided? What is the use case for knowing whether a value was inserted.You may need to perform extra logic when value is inserted in the container and something else when value already existed. I have a special version of function for that: Value* getOrCreate(Key key, out bool wasCreated, Value default_value = Value.init) here is an example of usage: https://github.com/MrSmith33/voxelman/blob/master/plugins/voxelman/entity/entityobservermanager.d#L33
Apr 17 2018
On Tuesday, 17 April 2018 at 19:33:16 UTC, MrSmith wrote:You may need to perform extra logic when value is inserted in the container and something else when value already existed.Fair enough, I will consider adding this and some tests to the PR, a function overload seems like the way to go.
Apr 17 2018
On 4/17/18 12:18 PM, Nick Treleaven wrote:On Sunday, 15 April 2018 at 22:52:47 UTC, Giles Bathgate wrote:Not as straightforward, but it can be done: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; }); Note that most of the time, the only reason you need to know it's new is to initialize it. But the lazy parameter takes care of that for you.The function provides a means to get a value corresponding to the key, but if the value doesn't exist it will evaluate the lazy argument to create a new value, add this to the associative array and then return it. auto p = lookup.getOrAdd("giles", new Person);Thanks for making this pull, I've thought about solving this before. I think the function needs to provide a way to tell if the value was already present.I also think it's more ergonomic not to have to use a lazy argument, and probably more efficient, so I had in mind: Value* slot(AA aa, K key, scope bool* inserted = null);I like the name slot. I'm less enthused about the extra machinery needed for initializing the value. Why do you think it's less efficient to use a lazy parameter?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. -Steve
Apr 17 2018
On Tuesday, 17 April 2018 at 20:49:30 UTC, Steven Schveighoffer wrote:Not as straightforward, but it can be done: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; });Yes, I like that approach. I don't want to bloat the feature at this stage, although there is nothing stopping adding an overload later. I agree returning ref makes sense since we never return null. Consequently, I am leaning toward thinking it should be called getOrCreate now though. Rust calls its version of this function `or_insert_with` (blegh) https://doc.rust-lang.org/std/collections/hash_map/enum.Entry.html#method.or_insert_with
Apr 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.
Apr 17 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?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`.)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.
Apr 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); ¯\_(ツ)_/¯
Apr 18 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.
Apr 20 2018
On Friday, 20 April 2018 at 08:37:59 UTC, Nick Treleaven wrote: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.So the low-level API you are requesting is part of the pull request, the low-level API is called _aaGetX, whereas the previous API was _aaGetY, the new API adds a boolean out parameter `found` and if you really wanted to you could do this: ----- extern (C) void* _aaGetX(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey, out bool found) pure nothrow; V* slot(K, V)(ref V[K] aa, K key, out bool found) { return cast(V*) _aaGetX(cast(void**)&aa, typeid(V[K]), V.sizeof, &key, found); } ----- I am also benchmarking the `update` function I talked about previously. https://gist.github.com/GilesBathgate/8409b5889ebb7b1302627c50f342a28b The results seem to indicate that there isn't much in it performance wise. test1 [56 ms, 377 µs, and 4 hnsecs] test2 [56 ms, 262 µs, and 2 hnsecs] I can't fathom why the delegates version is faster, but I am just putting it down to luck ;)
Apr 20 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.
Apr 18 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
Apr 20 2018
On 04/20/2018 10:24 AM, Nick Treleaven wrote:On Wednesday, 18 April 2018 at 16:47:50 UTC, ag0aep6g wrote:Hm. Do you know why that's not allowed? I can't see how it would be less safe than returning a pointer, and I can't find a mention of it in DIP 1000 [1]. Maybe it's just a case of incomplete/buggy implementation? [1] https://github.com/dlang/DIPs/blob/master/DIPs/DIP1000.mdYou 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
Apr 20 2018
On Friday, April 20, 2018 10:56:37 ag0aep6g via Digitalmars-d wrote:On 04/20/2018 10:24 AM, Nick Treleaven wrote:The compiler assumes that a pointer is valid when determining whether code is safe. It then disallows operations that it can't guarantee are safe even if the pointer is valid, and it disallows taking the address in cases where it can't guarantee that that's safe. Basically, it verifies the safety of pointers when they're created and then assumes that they're safe after that. So, passing around a pointer is perfectly safe, whereas taking the address of a ref return value to get a pointer is not. - Jonathan M DavisOn Wednesday, 18 April 2018 at 16:47:50 UTC, ag0aep6g wrote:Hm. Do you know why that's not allowed? I can't see how it would be less safe than returning a pointer, and I can't find a mention of it in DIP 1000 [1]. Maybe it's just a case of incomplete/buggy implementation? [1] https://github.com/dlang/DIPs/blob/master/DIPs/DIP1000.mdYou 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
Apr 20 2018
On Friday, 20 April 2018 at 09:24:26 UTC, Jonathan M Davis wrote:The compiler assumes that a pointer is valid when determining whether code is safe.[...]Basically, it verifies the safety of pointers when they're created and then assumes that they're safe after that.Can't it do the same for ref returns? Unsafe stuff like returning a ref to a local is already disallowed, just like returning a pointer to a local. So the compiler checks the safety on creation, at least in some cases.So, passing around a pointer is perfectly safe, whereas taking the address of a ref return value to get a pointer is not.It's still not clear to me how taking the address of a ref return is necessarily less safe than using a returned pointer. Do you maybe have an example in code that shows the difference? It seems to me that refs and pointers are very much equivalent. I should always be able to turn one into the other.
Apr 20 2018
On 4/20/18 4:24 AM, Nick Treleaven wrote:On Wednesday, 18 April 2018 at 16:47:50 UTC, ag0aep6g wrote:Hm... I would have expected it to work in dip1000. Hard for me to understand what dip1000 is doing, but it seems like an omission: safe ref int foo(return ref int x) { return x; } void main() safe { int x; scope int *xp = &x; // OK with dip1000 xp = foo(x); // Error, even though it's exactly the same } And in this case, it doesn't even need to be scope, as it's GC data. How does one communicate that the ref return is of GC data and can be escaped as much as you want? -SteveYou 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
Apr 20 2018
On Friday, 20 April 2018 at 14:02:17 UTC, Steven Schveighoffer wrote:On 4/20/18 4:24 AM, Nick Treleaven wrote:The error seems to be about type mismatching, it works if you use auto. I don't see how you could convert the `ref int` to `int *` https://run.dlang.io/is/eIJMIKOn Wednesday, 18 April 2018 at 16:47:50 UTC, ag0aep6g wrote:Hm... I would have expected it to work in dip1000. Hard for me to understand what dip1000 is doing, but it seems like an omission: safe ref int foo(return ref int x) { return x; } void main() safe { int x; scope int *xp = &x; // OK with dip1000 xp = foo(x); // Error, even though it's exactly the same } And in this case, it doesn't even need to be scope, as it's GC data. How does one communicate that the ref return is of GC data and can be escaped as much as you want? -SteveYou 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
Apr 20 2018
On 4/20/18 10:34 AM, Uknown wrote:On Friday, 20 April 2018 at 14:02:17 UTC, Steven Schveighoffer wrote:Sorry, it was a typo. In my real code I have: xp = &(foo(x)); That's what I get for hand-typing instead of copy-pasting :) -SteveOn 4/20/18 4:24 AM, Nick Treleaven wrote:The error seems to be about type mismatching, it works if you use auto. I don't see how you could convert the `ref int` to `int *` https://run.dlang.io/is/eIJMIKOn Wednesday, 18 April 2018 at 16:47:50 UTC, ag0aep6g wrote:Hm... I would have expected it to work in dip1000. Hard for me to understand what dip1000 is doing, but it seems like an omission: safe ref int foo(return ref int x) { return x; } void main() safe { int x; scope int *xp = &x; // OK with dip1000 xp = foo(x); // Error, even though it's exactly the same } And in this case, it doesn't even need to be scope, as it's GC data. How does one communicate that the ref return is of GC data and can be escaped as much as you want?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
Apr 20 2018
On Friday, 20 April 2018 at 15:00:39 UTC, Steven Schveighoffer wrote:On 4/20/18 10:34 AM, Uknown wrote:It seems like a bug. Changing foo to take and return an `int *` works as expected https://run.dlang.io/is/3NYXYvOn Friday, 20 April 2018 at 14:02:17 UTC, Steven Schveighoffer wrote:Sorry, it was a typo. In my real code I have: xp = &(foo(x)); That's what I get for hand-typing instead of copy-pasting :) -SteveOn 4/20/18 4:24 AM, Nick Treleaven wrote:On Wednesday, 18 April 2018 at 16:47:50 UTC, ag0aep6g wrote:[...]
Apr 20 2018
On 4/17/18 4:49 PM, Steven Schveighoffer wrote:On 4/17/18 12:18 PM, Nick Treleaven wrote:Let me say I was surprised that this doesn't actually work. An in-line lambda will NOT work as a lazy parameter, even though that's EXACTLY what a lazy parameter is implemented as! And variadic lazy parameters are explicitly typed this way. Has that ever worked? I could have sworn it did... -SteveThanks for making this pull, I've thought about solving this before. I think the function needs to provide a way to tell if the value was already present.Not as straightforward, but it can be done: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; });
Apr 20 2018
On Friday, April 20, 2018 16:35:43 Steven Schveighoffer via Digitalmars-d wrote:On 4/17/18 4:49 PM, Steven Schveighoffer wrote:I'm not sure. I mucked around with lazy like this when I was originally working on stuff like assertThrown and assertPred 7 or 8 years ago, but I've done _very_ little with lazy since then. My gut reaction is that it worked, but it might not have. If you add parens after it so that you call it, it does work, since then the result is the correct type. And if you ignore the exact details of how lazy is implemented and consider that it's supposed to take an expression that evaluates to a specific type, a lambda doesn't match that unless it's called. So, it does make sense from that perspective and is likely why it works the way it does. The compiler would basically have to special-case delegates to accept stuff like lambdas when the type of the lazy parameter is not a delegate. And if it _did_ special-case it, then things might get interesting if you actually had a lazy parameter that was a delegate. So, I don't know if it should work or not, but the workaround is pretty simple - just add parens to call it. - Jonathan M DavisOn 4/17/18 12:18 PM, Nick Treleaven wrote:Let me say I was surprised that this doesn't actually work. An in-line lambda will NOT work as a lazy parameter, even though that's EXACTLY what a lazy parameter is implemented as! And variadic lazy parameters are explicitly typed this way. Has that ever worked? I could have sworn it did...Thanks for making this pull, I've thought about solving this before. I think the function needs to provide a way to tell if the value was already present.Not as straightforward, but it can be done: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; });
Apr 20 2018
On 4/20/18 5:40 PM, Jonathan M Davis wrote:On Friday, April 20, 2018 16:35:43 Steven Schveighoffer via Digitalmars-d wrote:But, it *is* a delegate. literally, that's what gets implemented (and it has to be that way). I suppose if it's inlined, the compiler could take some leeway, but that is the same with a delegate literal passed to a function that takes a delegate anyway. The fact that lazy variadics are explicitly delegates makes this even more annoying. I wish we could have the best of both worlds (non-ugly calls inside the function, and easy binding to whatever you want at the call side). I'm not even sure why lazy variadics are the way they are, I can instantly think of a better syntax for them.On 4/17/18 4:49 PM, Steven Schveighoffer wrote:I'm not sure. I mucked around with lazy like this when I was originally working on stuff like assertThrown and assertPred 7 or 8 years ago, but I've done _very_ little with lazy since then. My gut reaction is that it worked, but it might not have. If you add parens after it so that you call it, it does work, since then the result is the correct type. And if you ignore the exact details of how lazy is implemented and consider that it's supposed to take an expression that evaluates to a specific type, a lambda doesn't match that unless it's called. So, it does make sense from that perspective and is likely why it works the way it does.On 4/17/18 12:18 PM, Nick Treleaven wrote:Let me say I was surprised that this doesn't actually work. An in-line lambda will NOT work as a lazy parameter, even though that's EXACTLY what a lazy parameter is implemented as! And variadic lazy parameters are explicitly typed this way. Has that ever worked? I could have sworn it did...Thanks for making this pull, I've thought about solving this before. I think the function needs to provide a way to tell if the value was already present.Not as straightforward, but it can be done: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; });The compiler would basically have to special-case delegates to accept stuff like lambdas when the type of the lazy parameter is not a delegate. And if it _did_ special-case it, then things might get interesting if you actually had a lazy parameter that was a delegate.This is pretty easy, does your delegate return a delegate or an int? ;) This is a super-solvable problem, I'm just surprised it wasn't this way before, I could have sworn I've done this in the past.So, I don't know if it should work or not, but the workaround is pretty simple - just add parens to call it.This results in a double delegate call -- the compiler makes a delegate that calls your delegate. Not optimal, but yes it does work. -Steve
Apr 20 2018
On Friday, April 20, 2018 19:36:57 Steven Schveighoffer via Digitalmars-d wrote:On 4/20/18 5:40 PM, Jonathan M Davis wrote:If we want to make the compiler accept it, then I'm not necessarily against it, but arguably, the fact that it uses a delegate is an implementation detail. In principle, what you're doing with a lazy parameter is just saying that the expression being passed to it isn't immediately evaluated. Having it then accept expressions that don't result in the type of the lazy parameter is then pretty weird.On Friday, April 20, 2018 16:35:43 Steven Schveighoffer via Digitalmars-d wrote:But, it *is* a delegate. literally, that's what gets implemented (and it has to be that way). I suppose if it's inlined, the compiler could take some leeway, but that is the same with a delegate literal passed to a function that takes a delegate anyway.On 4/17/18 4:49 PM, Steven Schveighoffer wrote:I'm not sure. I mucked around with lazy like this when I was originally working on stuff like assertThrown and assertPred 7 or 8 years ago, but I've done _very_ little with lazy since then. My gut reaction is that it worked, but it might not have. If you add parens after it so that you call it, it does work, since then the result is the correct type. And if you ignore the exact details of how lazy is implemented and consider that it's supposed to take an expression that evaluates to a specific type, a lambda doesn't match that unless it's called. So, it does make sense from that perspective and is likely why it works the way it does.On 4/17/18 12:18 PM, Nick Treleaven wrote:Let me say I was surprised that this doesn't actually work. An in-line lambda will NOT work as a lazy parameter, even though that's EXACTLY what a lazy parameter is implemented as! And variadic lazy parameters are explicitly typed this way. Has that ever worked? I could have sworn it did...Thanks for making this pull, I've thought about solving this before. I think the function needs to provide a way to tell if the value was already present.Not as straightforward, but it can be done: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; });The fact that lazy variadics are explicitly delegates makes this even more annoying. I wish we could have the best of both worlds (non-ugly calls inside the function, and easy binding to whatever you want at the call side). I'm not even sure why lazy variadics are the way they are, I can instantly think of a better syntax for them.Well, this thread is the first that I've heard of "lazy variadics." Until I saw your other post about them, I assumed that you meant something like auto foo(Args...)(lazy Args args) { ... } I would haven't have assumed that void foo(int delegate()[] dgs...) { dgs[0](); } had anything to do with lazy, and the fact that it works with anything that doesn't implictly convert to a delegate seems like a bug to me, especially when void foo(int delegate() dg) { dg(); } doesn't compile when it's passed an int. IMHO, they should be consistent.Of course it's solvable, but that doesn't mean that it's ultimately a good idea. It does have corner cases that may or may not be a problem. e.g. something like void foo(lazy int delegate() dg) { } could be a problem if void foo(lazy int i) { } accepts delegates whose result is an int. I'm not necessarily against making it possible, but I would consider the fact that lazy is implemented via a delegate to be an implementation detail of lazy, and if lazy parameters started accepting lambdas whose result was the right type, then that means that auto foo(string str) {...} and auto foo(lazy string str) {...} would accept different arguments even though their parameters are the same type, and I question that that's a good idea. I'm sure that it would work on some level, and the corner cases probably wouldn't matter often, but it seems much cleaner to me if lazy has no effect on what is accepted and just has an effect on whether that code is run immediately.The compiler would basically have to special-case delegates to accept stuff like lambdas when the type of the lazy parameter is not a delegate. And if it _did_ special-case it, then things might get interesting if you actually had a lazy parameter that was a delegate.This is pretty easy, does your delegate return a delegate or an int? ;) This is a super-solvable problem, I'm just surprised it wasn't this way before, I could have sworn I've done this in the past.Sure, it's sub-optimal, and I would hope that the compiler would optimize it accordingly (though it wouldn't surprise me if it doesn't), but it works without having to change anything, and it would presumably continue to work even if we did change the behavior of lazy to accept lambdas whose return type was the type of the parameter. - Jonathan M DavisSo, I don't know if it should work or not, but the workaround is pretty simple - just add parens to call it.This results in a double delegate call -- the compiler makes a delegate that calls your delegate. Not optimal, but yes it does work.
Apr 20 2018
On Friday, 20 April 2018 at 20:35:43 UTC, Steven Schveighoffer wrote:Let me say I was surprised that this doesn't actually work. An in-line lambda will NOT work as a lazy parameter, even though that's EXACTLY what a lazy parameter is implemented as! And variadic lazy parameters are explicitly typed this way.I kind of expected it to work too. However, I just created a template to allow this to work with my PR. https://github.com/dlang/druntime/pull/2162/files#diff-a68e58fcf0de5aa198fcaceafe4e8cf9R2344 Getting it right took a few head-scratchers, but I put that down to my lack of experience with D ;)
Apr 20 2018
On Friday, April 20, 2018 22:03:04 Giles Bathgate via Digitalmars-d wrote:On Friday, 20 April 2018 at 20:35:43 UTC, Steven Schveighoffer wrote:Honestly, I think that it's a terrible idea to special-case it like that. If we want to argue for making it work in the language, that's fine, but if we special-case it like this, then it will work with some functions that have lazy parameters and not others, and the result will be confusing. Besides, all it takes to be able to pass a lamdba or delegate to a lazy parameter is to actually call it when passing it. So, if you add parens after the braces, it works. There's no need to go and add a special case for it to the function. - Jonathan M DavisLet me say I was surprised that this doesn't actually work. An in-line lambda will NOT work as a lazy parameter, even though that's EXACTLY what a lazy parameter is implemented as! And variadic lazy parameters are explicitly typed this way.I kind of expected it to work too. However, I just created a template to allow this to work with my PR. https://github.com/dlang/druntime/pull/2162/files#diff-a68e58fcf0de5aa198f caceafe4e8cf9R2344 Getting it right took a few head-scratchers, but I put that down to my lack of experience with D ;)
Apr 20 2018
On Friday, 20 April 2018 at 22:21:13 UTC, Jonathan M Davis wrote:Honestly, I think that it's a terrible idea to special-case it like that. If we want to argue for making it work in the language, that's fine, but if we special-case it like this, then it will work with some functions that have lazy parameters and not others, and the result will be confusing. Besides, all it takes to be able to pass a lamdba or delegate to a lazy parameter is to actually call it when passing it. So, if you add parens after the braces, it works. There's no need to go and add a special case for it to the function.Again lack of experience, so I presume you can just do: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; }()); I hadn't realised that until now. I enjoy your brutal honesty by the way ;)
Apr 20 2018
On Friday, April 20, 2018 22:46:54 Giles Bathgate via Digitalmars-d wrote:On Friday, 20 April 2018 at 22:21:13 UTC, Jonathan M Davis wrote:Yes. That should work. e.g. this import std.stdio; void foo(lazy string l) { } void main() { foo({writeln("foo"); return "str";}()); } compiles and runs just fine without printing anything, whereas you get a compilation error if you don't have the parens after the closing brace. - Jonathan M DavisHonestly, I think that it's a terrible idea to special-case it like that. If we want to argue for making it work in the language, that's fine, but if we special-case it like this, then it will work with some functions that have lazy parameters and not others, and the result will be confusing. Besides, all it takes to be able to pass a lamdba or delegate to a lazy parameter is to actually call it when passing it. So, if you add parens after the braces, it works. There's no need to go and add a special case for it to the function.Again lack of experience, so I presume you can just do: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; }()); I hadn't realised that until now. I enjoy your brutal honesty by the way ;)
Apr 20 2018
On Friday, 20 April 2018 at 23:13:58 UTC, Jonathan M Davis wrote:Yes. That should work. e.g. this import std.stdio; void foo(lazy string l) { } void main() { foo({writeln("foo"); return "str";}()); } compiles and runs just fine without printing anything, whereas you get a compilation error if you don't have the parens after the closing brace.Ok cool. I've updated the PR since basically, I agree with your points about it being confusing to cater for the special case.
Apr 20 2018
On 4/20/18 7:21 PM, Giles Bathgate wrote:On Friday, 20 April 2018 at 23:13:58 UTC, Jonathan M Davis wrote:Yeah, I didn't mean for the special case to happen -- I thought it just worked! I should have tested my original code... Sorry. -SteveYes. That should work. e.g. this import std.stdio; void foo(lazy string l) { } void main() { foo({writeln("foo"); return "str";}()); } compiles and runs just fine without printing anything, whereas you get a compilation error if you don't have the parens after the closing brace.Ok cool. I've updated the PR since basically, I agree with your points about it being confusing to cater for the special case.
Apr 20 2018
On 4/20/18 6:46 PM, Giles Bathgate wrote:On Friday, 20 April 2018 at 22:21:13 UTC, Jonathan M Davis wrote:The drawback here, of course, is that it's a lambda calling a lambda (if you end up using the value). But of course, your overload was the same thing. I'm just surprised it doesn't work, especially when this works: // lazy variadic void foo(int delegate()[] dgs...) { dgs[0](); } foo(1); // ok, same as { return 1; } foo({inserted = true; return 1;}); // ok Of course, it's not as nice syntax inside the function. -SteveHonestly, I think that it's a terrible idea to special-case it like that. If we want to argue for making it work in the language, that's fine, but if we special-case it like this, then it will work with some functions that have lazy parameters and not others, and the result will be confusing. Besides, all it takes to be able to pass a lamdba or delegate to a lazy parameter is to actually call it when passing it. So, if you add parens after the braces, it works. There's no need to go and add a special case for it to the function.Again lack of experience, so I presume you can just do: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; }()); I hadn't realised that until now. I enjoy your brutal honesty by the way ;)
Apr 20 2018
On Friday, April 20, 2018 19:27:20 Steven Schveighoffer via Digitalmars-d wrote:On 4/20/18 6:46 PM, Giles Bathgate wrote:Honestly, I would have considered it a bug that it accepts 1, since that's not a delegate or lambda or anything of the sort, and the function is explicitly typed to take delegates. - Jonathan M DavisOn Friday, 20 April 2018 at 22:21:13 UTC, Jonathan M Davis wrote:The drawback here, of course, is that it's a lambda calling a lambda (if you end up using the value). But of course, your overload was the same thing. I'm just surprised it doesn't work, especially when this works: // lazy variadic void foo(int delegate()[] dgs...) { dgs[0](); } foo(1); // ok, same as { return 1; } foo({inserted = true; return 1;}); // ok Of course, it's not as nice syntax inside the function.Honestly, I think that it's a terrible idea to special-case it like that. If we want to argue for making it work in the language, that's fine, but if we special-case it like this, then it will work with some functions that have lazy parameters and not others, and the result will be confusing. Besides, all it takes to be able to pass a lamdba or delegate to a lazy parameter is to actually call it when passing it. So, if you add parens after the braces, it works. There's no need to go and add a special case for it to the function.Again lack of experience, so I presume you can just do: bool inserted = false; auto p = aa.getOrAdd("key", {inserted = true; return new Person; }()); I hadn't realised that until now. I enjoy your brutal honesty by the way ;)
Apr 20 2018
On Sunday, 15 April 2018 at 22:52:47 UTC, Giles Bathgate wrote:Hi, I wanted a way to lazily insert a value into an associative array, and I am proposing a new function called getOrAdd in https://github.com/dlang/druntime/pull/2162Yes please :) Just one question: does this work for value-types? This is a pretty common case for me (simplified and not optimized version): size_t[string] counter; ... foreach(e; elements) { if (e.something !in counter) counter[e.something] = 0; else counter[e.something]++; } So will this work? foreach(e; elements) counter.update(e.something, { return 0; }, (size_t v) { return v+1; }) Andrea Andrea
Apr 24 2018
On Tuesday, 24 April 2018 at 09:09:37 UTC, Andrea Fontana wrote:Just one question: does this work for value-types?The intention is that it will work for value types. I will add some unit tests to check that use case. - Giles
Apr 25 2018