April 20, 2018
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...

-Steve
April 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:
> > 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. 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 Davis

April 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 ;)


April 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:
> > 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-a68e58fcf0de5aa198f caceafe4e8cf9R2344
>
> Getting it right took a few head-scratchers, but I put that down to my lack of experience with D ;)

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 Davis

April 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 ;)


April 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:
> > 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 ;)

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 Davis

April 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.


April 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:
>> 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.

-Steve
April 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:
>> 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.

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.

> 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
April 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:
>> 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.
> 
> 

Yeah, I didn't mean for the special case to happen -- I thought it just worked!

I should have tested my original code... Sorry.

-Steve