Thread overview
Chained method argument evaluation order
Jan 16, 2008
Sean Kelly
Jan 17, 2008
Jason House
January 16, 2008
Something tells me this was discussed before, but sheesh..

int a() { Stdout.formatln("A"); return 1; }
int b() { Stdout.formatln("B"); return 2; }

struct S
{
    S* chain(int x)
    {
        Stdout.formatln("{}", x);
        return this;
    }
}

void main()
{
    S s;
    s.chain(a()).chain(b());
}

This prints the following compiled with DMDWin:

B
A
1
2

Notice that the chained methods are called in the right order, but that their arguments -- even though they're in different function calls!! -- are evaluated in _reverse_ order, and _before_ any of the chained methods are called.

I really wouldn't have expected this.  I _would_ have expected

A
1
B
2

But the compiler must be being clever here, for some reason.

Should this kind of thing be documented, specified, implementation-dependent etc.?  Because I would have expected the chained call above to basically be evaluated as:

auto t = s.chain(a());
t.chain(b());

which gives the expected output above.


January 16, 2008
Jarrett Billingsley wrote:
> Something tells me this was discussed before, but sheesh..
> 
> int a() { Stdout.formatln("A"); return 1; }
> int b() { Stdout.formatln("B"); return 2; }
> 
> struct S
> {
>     S* chain(int x)
>     {
>         Stdout.formatln("{}", x);
>         return this;
>     }
> }
> 
> void main()
> {
>     S s;
>     s.chain(a()).chain(b());
> }
> 
> This prints the following compiled with DMDWin:
> 
> B
> A
> 1
> 2
> 
> Notice that the chained methods are called in the right order, but that their arguments -- even though they're in different function calls!! -- are evaluated in _reverse_ order, and _before_ any of the chained methods are called.
> 
> I really wouldn't have expected this.  I _would_ have expected
> 
> A
> 1
> B
> 2
> 
> But the compiler must be being clever here, for some reason.
> 
> Should this kind of thing be documented, specified, implementation-dependent etc.?

I think this is actually documented here:

http://www.digitalmars.com/d/1.0/expression.html

In the "Expression Order" section.  Basically, I think that:

a.op(b).op(c)

is equivalent to:

(a + b) + c

In terms of evaluation.  ie. I think you can be sure that the a.op(b) function will be called first and that b will be evaluated before this call takes place, but that's it.  The compiler is free to evaluate b and c both before this call, and to do so in any order.  However, I think Walter is planning on changing this for 2.0.


Sean
January 17, 2008
Jarrett Billingsley wrote:
> I really wouldn't have expected this.

I'd expect the calls to be done in an implementation-dependent order.  It reminds me of stuff like "a = b++ + b++;" which is a classic example of undefined behavior.