April 20, 2018 Re: Why don't lazy parameters bind to delegates? Was: Feature to get or add value to an associative array. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Friday, April 20, 2018 19:27:20 Steven Schveighoffer via Digitalmars-d wrote:
> On 4/20/18 6:46 PM, Giles Bathgate wrote:
> > 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 ;)
> 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 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 Davis
|
April 20, 2018 Re: Why don't lazy parameters bind to delegates? Was: Feature to get or add value to an associative array. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | 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: > > On Friday, April 20, 2018 16:35:43 Steven Schveighoffer via Digitalmars-d > > > > wrote: > >> On 4/17/18 4:49 PM, Steven Schveighoffer wrote: > >>> On 4/17/18 12:18 PM, Nick Treleaven wrote: > >>>> 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; }); > >> > >> 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... > > > > 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. > > 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. 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. > 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. > > 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. 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. > > 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. 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 Davis |
April 24, 2018 Re: Feature to get or add value to an associative array. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Giles Bathgate | 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/2162
Yes 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
|
April 25, 2018 Re: Feature to get or add value to an associative array. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrea Fontana | 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
|
Copyright © 1999-2021 by the D Language Foundation