Thread overview
How does laziness and UFCS interact?
Mar 09, 2015
Logan Capaldo
Mar 09, 2015
Ali Çehreli
Mar 10, 2015
Logan Capaldo
Mar 10, 2015
John Colvin
Mar 10, 2015
Ali Çehreli
March 09, 2015
I just became aware of http://dlang.org/phobos/std_exception.html#.ifThrown . It's neat, but it seems non-obvious to me how lazy + UFCS should work in general.

consider


void lazily(T)(lazy T expression)
{
   expression();
}

It's clear when saying lazily(a.b().c()); that the whole of "a.b().c()" is going to be evaluated lazily. With UFCS though, I'm more unsure:

is a.b().c().lazily() -> lazily(a.b().c()) or is it more akin to

auto tmp = a.b();
lazily(tmp.c());

If the later is it possible to force the former while still using UFCS, ie is (a.b().c()).lazily() different than a.b().c().lazily()?


March 09, 2015
On 03/09/2015 03:03 PM, Logan Capaldo wrote:
> I just became aware of
> http://dlang.org/phobos/std_exception.html#.ifThrown . It's neat, but it
> seems non-obvious to me how lazy + UFCS should work in general.
>
> consider
>
>
> void lazily(T)(lazy T expression)
> {
>     expression();
> }
>
> It's clear when saying lazily(a.b().c()); that the whole of "a.b().c()"
> is going to be evaluated lazily. With UFCS though, I'm more unsure:
>
> is a.b().c().lazily() -> lazily(a.b().c()) or is it more akin to
>
> auto tmp = a.b();
> lazily(tmp.c());
>
> If the later is it possible to force the former while still using UFCS,
> ie is (a.b().c()).lazily() different than a.b().c().lazily()?
>
>

You are right. I had the same observation at minute 11:27 below, where I warn against UFCS with assumeWontThrow:


http://www.youtube.com/watch?feature=player_detailpage&v=oF8K4-bieaw#t=687

Ali

March 10, 2015
On Monday, 9 March 2015 at 22:15:43 UTC, Ali Çehreli wrote:
> You are right. I had the same observation at minute 11:27 below, where I warn against UFCS with assumeWontThrow:
>
>
> http://www.youtube.com/watch?feature=player_detailpage&v=oF8K4-bieaw#t=687
>
> Ali

Sorry, which is right? I know ifThrown is lazy, I'm curious about the "amount" of the expression that is evaluated lazily.


  a.b().c().assumeWontThrow     vs.  a.b().c().assumeWontThrow
  ^--+----^                                ^-^
     |                                      |
     +- lazy?                               +- lazy?


The video seems to say "don't use lazy functions with UFCS because you might think the lazy part gets evaluated first, when it does not". Seems reasonable, although I don't know it's any different than assumeWontThrow(f()).
March 10, 2015
On Tuesday, 10 March 2015 at 14:41:00 UTC, Logan Capaldo wrote:
> On Monday, 9 March 2015 at 22:15:43 UTC, Ali Çehreli wrote:
>> You are right. I had the same observation at minute 11:27 below, where I warn against UFCS with assumeWontThrow:
>>
>>
>> http://www.youtube.com/watch?feature=player_detailpage&v=oF8K4-bieaw#t=687
>>
>> Ali
>
> Sorry, which is right? I know ifThrown is lazy, I'm curious about the "amount" of the expression that is evaluated lazily.
>
>
>   a.b().c().assumeWontThrow     vs.  a.b().c().assumeWontThrow
>   ^--+----^                                ^-^
>      |                                      |
>      +- lazy?                               +- lazy?
>
>
> The video seems to say "don't use lazy functions with UFCS because you might think the lazy part gets evaluated first, when it does not". Seems reasonable, although I don't know it's any different than assumeWontThrow(f()).

a.b().c().assumeWontThrow is rewritten as assumeWontThrow(a.b().c()) and therefore the whole chain is lazy.
March 10, 2015
On 03/10/2015 08:00 AM, John Colvin wrote:

> On Tuesday, 10 March 2015 at 14:41:00 UTC, Logan Capaldo wrote:
>> On Monday, 9 March 2015 at 22:15:43 UTC, Ali Çehreli wrote:
>>> You are right. I had the same observation at minute 11:27 below,
>>> where I warn against UFCS with assumeWontThrow:
>>>
>>>
>>> http://www.youtube.com/watch?feature=player_detailpage&v=oF8K4-bieaw#t=687
>>>
>>>
>>> Ali
>>
>> Sorry, which is right?

You are right that it is confusing. :)

>> I know ifThrown is lazy, I'm curious about the
>> "amount" of the expression that is evaluated lazily.
>>
>>
>>   a.b().c().assumeWontThrow     vs.  a.b().c().assumeWontThrow
>>   ^--+----^                                ^-^
>>      |                                      |
>>      +- lazy?                               +- lazy?

As John Colvin said, the first one is right. A test:

import std.stdio;

struct S
{}

S a(S s)
{
    writeln("entered a");
    return s;
}

S b(S s)
{
    writeln("entered b");
    return s;
}

void lazily(T)(lazy T expression)
{
    writeln("entered lazily");
    expression();
}

void main()
{
    S().a.b.lazily;
}

Outputs

entered lazily
entered a
entered b


>> The video seems to say "don't use lazy functions with UFCS because you
>> might think the lazy part gets evaluated first, when it does not".
>> Seems reasonable, although I don't know it's any different than
>> assumeWontThrow(f()).

You are right again. :) However, putting the lazy-taking function "outside" the whole expression makes it visible right away, making easy for me to realize that the execution order may be different from common chains.

Ali

March 10, 2015
On Tuesday, 10 March 2015 at 17:42:37 UTC, Ali Çehreli wrote:
> You are right again. :) However, putting the lazy-taking function "outside" the whole expression makes it visible right away, making easy for me to realize that the execution order may be different from common chains.

"lazy" aka "named parameters" semantically works just like macros using textual substitution. Just imagine that it is inlined in situ and it becomes clear what is happening.

I believe it was the common parameter transfer mode in Algol, but programmers found it terribly confusing, so just about all languages that followed have avoided it. So if anyone gets confused, then find some comfort in knowing that people found it confusing 50 years ago too. :^)