March 30, 2007
James Dennett wrote:
> kris wrote:
[snip]
>>There never was any argument of which you claim. I simply noted that
>>eval-order had been clarified before, using your usage of "eval-order"
>>from within the same post. If you revisit, you'll see that was actually
>> referring to call-chaining instead, so there's perhaps a misuse of terms:
>>
>>   Cout.opCall("Hello, ").opCall(Cin.get);
>>
>>As you can see, there is only one parameter passed to each call, and
>>therefore the order of /parameter/ eval is "not at stake here" (as I
>>noted to Frits). 
> 
> 
> There are two arguments to the second opCall.  One is
> the result of Cout.opCall("Hello, ") and the other is
> the result of Cin.get, and they can be evaluated in
> either order unless some rule prohibits it.

I think you'll find that call-chaining does not operate in that manner, James? If you look a bit closer, you'll see that the lhs has to be evaluated first, simply to get something to deref the rhs. Further, there is only one argument permitted to the opCall() itself

For a fuller description, I suggest you bring it up with Walter instead?

> Please, just address the technical content rather than
> flaming.

With pleasure, James Dennett
March 30, 2007
kris wrote:
>    Cout.opCall("Hello, ").opCall(Cin.get);

Given:
	a.f(b,c);
can be equivalently written as:
	f(a,b,c);

we can transform:
	Cout.opCall("Hello, ").opCall(Cin.get);
into:
	opCall(opCall(Cout,"Hello, "), Cin.get);

And it is transformed that way, because the optimizer/back end knows nothing about member functions. They're just more function calls.

The nested opCall will get called before the outer opCall, but otherwise the order is undefined (and will change depending on the function calling convention, whether it is a virtual call or not, etc.).

I suggest that at a minimum, if you wish to retain the current design, build a test into the test suite that verifies the required order of evaluation.

It's possible that in the future, to ensure source code portability of D, that the order of evaluation will become fixed. Such a change is a fair amount of work to accomplish, and will incur a runtime penalty (the implementation defined order allows the compiler to rearrange things to minimize register pressure). Even if the order was fixed, it still might not be in the right order for call chaining to work as your design needs it to.

I also suggest that, with the maturing of the variadic template capability of D, using it will side step the order of evaluation problem completely. It'll still be an aesthetically pleasing design to the user.
March 30, 2007
kris wrote:
> James Dennett wrote:
>> kris wrote:
> [snip]
>>> There never was any argument of which you claim. I simply noted that eval-order had been clarified before, using your usage of "eval-order" from within the same post. If you revisit, you'll see that was actually referring to call-chaining instead, so there's perhaps a misuse of terms:
>>>
>>>   Cout.opCall("Hello, ").opCall(Cin.get);
>>>
>>> As you can see, there is only one parameter passed to each call, and therefore the order of /parameter/ eval is "not at stake here" (as I noted to Frits).
>>
>>
>> There are two arguments to the second opCall.  One is
>> the result of Cout.opCall("Hello, ") and the other is
>> the result of Cin.get, and they can be evaluated in
>> either order unless some rule prohibits it.
> 
> I think you'll find that call-chaining does not operate in that manner, James? If you look a bit closer, you'll see that the lhs has to be evaluated first, simply to get something to deref the rhs. Further, there is only one argument permitted to the opCall() itself
> 
> For a fuller description, I suggest you bring it up with Walter instead?

Walter's post in this thread (<eui80b$19hl$1@digitalmars.com>) seems to confirm my viewpoint as quoted above.  If you still don't think so after reading his message, I'd be interested if you can explain where Walter's explanation differs from mine.

Regards,

James.
March 30, 2007
James Dennett wrote:
> kris wrote:
> 
>>James Dennett wrote:
>>
>>>kris wrote:
>>
>>[snip]
>>
>>>>There never was any argument of which you claim. I simply noted that
>>>>eval-order had been clarified before, using your usage of "eval-order"
>>>>from within the same post. If you revisit, you'll see that was actually
>>>>referring to call-chaining instead, so there's perhaps a misuse of
>>>>terms:
>>>>
>>>>  Cout.opCall("Hello, ").opCall(Cin.get);
>>>>
>>>>As you can see, there is only one parameter passed to each call, and
>>>>therefore the order of /parameter/ eval is "not at stake here" (as I
>>>>noted to Frits). 
>>>
>>>
>>>There are two arguments to the second opCall.  One is
>>>the result of Cout.opCall("Hello, ") and the other is
>>>the result of Cin.get, and they can be evaluated in
>>>either order unless some rule prohibits it.
>>
>>I think you'll find that call-chaining does not operate in that manner,
>>James? If you look a bit closer, you'll see that the lhs has to be
>>evaluated first, simply to get something to deref the rhs. Further,
>>there is only one argument permitted to the opCall() itself
>>
>>For a fuller description, I suggest you bring it up with Walter instead?
> 
> 
> Walter's post in this thread (<eui80b$19hl$1@digitalmars.com>)
> seems to confirm my viewpoint as quoted above.  If you still
> don't think so after reading his message, I'd be interested
> if you can explain where Walter's explanation differs from
> mine.
> 
> Regards,
> 
> James.

Simply because that's what Walter led us to believe a long time ago, James, and it is how the compiler is implemented. Don't know what else to tell you.
March 30, 2007
John Reimer wrote:
> On Thu, 29 Mar 2007 21:59:49 -0700, James Dennett wrote:
> 
>> kris wrote:
>>> Andrei Alexandrescu (See Website For Email) wrote:
>>>> kris wrote:
>>>>
>>>>> Frits van Bommel wrote:
>>>>>
>>>>>> Yes, call-chaining can only evaluate left-to-right, but the parameters *passed* to the calls can be evaluated in any order.
>>>>>
>>>>> That's not at stake here, as far as I'm aware?
>>>>
>>>> My understanding is that it was brought up by yourself in an attempt
>>>> to explain that Cout("Hello, ")(Cin.get) will work properly. There was
>>>> an old thread mentioned, which deals with another problem entirely. So
>>>> that doesn't apply. Now I understand that argument has been dropped
>>>> entirely, and that now there is an argument that Cout("Hello,
>>>> ")(Cin.get) works due to some other unmentioned reasons.
>>> Then I politely suggest you are either badly confused, being entirely disengeneous, or are drunk ;-)
>>>
>>> There never was any argument of which you claim. I simply noted that
>>> eval-order had been clarified before, using your usage of "eval-order"
>>> from within the same post. If you revisit, you'll see that was actually
>>>  referring to call-chaining instead, so there's perhaps a misuse of terms:
>>>
>>>    Cout.opCall("Hello, ").opCall(Cin.get);
>>>
>>> As you can see, there is only one parameter passed to each call, and therefore the order of /parameter/ eval is "not at stake here" (as I noted to Frits).
>> There are two arguments to the second opCall.  One is
>> the result of Cout.opCall("Hello, ") and the other is
>> the result of Cin.get, and they can be evaluated in
>> either order unless some rule prohibits it.
>>
>> Please, just address the technical content rather than flaming.
>>
>> -- James
> 
> 
> I disagree with your strange assertion that Kris is flaming.

I don't want to use a lot of bandwidth to discuss this,
but I'll show briefly that my assertion is nothing but
a straightforward one based on what was written.

Kris wrote in his response to Andrei:

> I politely suggest you are either badly confused, being entirely disengeneous, or are drunk ;-)

and

> you don't appear to be the slightest bit interested in trying to understand much

and

> your demeanour has been and continues to be that of
> someone with a hidden adgenda or with a axe to grind

and

> If you'll drop that attitute forthwith, I'll be happy
> to continue

Emotions don't cancel out comments, particularly not when
made in a post that explicitly accuses someone of acting
in a deceptive or self-serving manner and of having an
unacceptable attitude.

In the moderated newsgroups I read, such content would
be blocked during moderation, and the groups do much
better at avoiding non-technical fights because of it.

Now, please just accept that I consider such attacks on
a person to be inappropriate, that they meet definitions
of "flaming" that are in common use in my experience,
and that the quotations above show to my satisfaction
that the post from Kris fell into this classification.

I recognize Kris as a valued contributor to this
community, and hope that these notes can be taken in
a constructive spirit.  I'm just an interested party
here, not trying to lay down the law -- but I've seen
a fair number of newsgroup discussions and have some
claim to an understanding of some of the problems
that can arise and how to avoid them.

-- James
March 30, 2007
kris wrote:
> James Dennett wrote:
>> kris wrote:
>>
>>> James Dennett wrote:
>>>
>>>> kris wrote:
>>>
>>> [snip]
>>>
>>>>> There never was any argument of which you claim. I simply noted that
>>>>> eval-order had been clarified before, using your usage of "eval-order"
>>>>> from within the same post. If you revisit, you'll see that was
>>>>> actually
>>>>> referring to call-chaining instead, so there's perhaps a misuse of
>>>>> terms:
>>>>>
>>>>>  Cout.opCall("Hello, ").opCall(Cin.get);
>>>>>
>>>>> As you can see, there is only one parameter passed to each call, and therefore the order of /parameter/ eval is "not at stake here" (as I noted to Frits).
>>>>
>>>>
>>>> There are two arguments to the second opCall.  One is
>>>> the result of Cout.opCall("Hello, ") and the other is
>>>> the result of Cin.get, and they can be evaluated in
>>>> either order unless some rule prohibits it.
>>>
>>> I think you'll find that call-chaining does not operate in that manner, James? If you look a bit closer, you'll see that the lhs has to be evaluated first, simply to get something to deref the rhs. Further, there is only one argument permitted to the opCall() itself
>>>
>>> For a fuller description, I suggest you bring it up with Walter instead?
>>
>>
>> Walter's post in this thread (<eui80b$19hl$1@digitalmars.com>) seems to confirm my viewpoint as quoted above.  If you still don't think so after reading his message, I'd be interested if you can explain where Walter's explanation differs from mine.
>>
>> Regards,
>>
>> James.
> 
> Simply because that's what Walter led us to believe a long time ago, James, and it is how the compiler is implemented. Don't know what else to tell you.

I doubt that Walter lead you to believe that the order of evaluation was defined for the particular example code under discussion here.  ("Call chaining" is not the issue at hand; order of evaluation of function arguments is the relevant issue.)

Did you read the message of Walter's to which I referred?

Walter wrote, today:

> The nested opCall will get called before the outer opCall,
> but otherwise the order is undefined (and will change
> depending on the function calling convention, whether it
> is a virtual call or not, etc.).

This is very concrete and very specific, and supports what I wrote above.  Are you suggesting that you disagree with this?  (If so, with what do you disagree: that Walter wrote this, that it supports my viewpoint, or something else?)

-- James
March 30, 2007
Walter Bright wrote:
> kris wrote:
> 
>>    Cout.opCall("Hello, ").opCall(Cin.get);
> 
> 
> Given:
>     a.f(b,c);
> can be equivalently written as:
>     f(a,b,c);
> 
> we can transform:
>     Cout.opCall("Hello, ").opCall(Cin.get);
> into:
>     opCall(opCall(Cout,"Hello, "), Cin.get);
> 
> And it is transformed that way, because the optimizer/back end knows nothing about member functions. They're just more function calls.
> 
> The nested opCall will get called before the outer opCall, but otherwise the order is undefined (and will change depending on the function calling convention, whether it is a virtual call or not, etc.).

Thank you for that clarification, Walter.

You note that the inner opCall will be evaluated before the outer one; how is x.one(a).two(b).three(c) tranformed?


> I suggest that at a minimum, if you wish to retain the current design, build a test into the test suite that verifies the required order of evaluation.
> 
> It's possible that in the future, to ensure source code portability of D, that the order of evaluation will become fixed. Such a change is a fair amount of work to accomplish, and will incur a runtime penalty (the implementation defined order allows the compiler to rearrange things to minimize register pressure). Even if the order was fixed, it still might not be in the right order for call chaining to work as your design needs it to.

Do you mean in terms of generic call-chaining, or that example specifically? For example, will the code x.one().two().three() always execute one() before two() ?


> I also suggest that, with the maturing of the variadic template capability of D, using it will side step the order of evaluation problem completely. It'll still be an aesthetically pleasing design to the user.

That's certainly a possibility, Walter, but call-chaining is surely expected to operate in an orderly fashion regardless of whether it is used as an IO interface or not? For example, what happens if two() were to return a different object for three() to be applied against? It would surely be incorrect to generate code whereby three() were called against x instead? In other words, for call chaining to operate correctly the following pseudocode should likely hold true:

# auto result = x.one().two().three();

auto tmp1 = x.one();
auto tmp2 = tmp1.two();
auto result = tmp2.three();

Is that incorrect?
March 30, 2007
On Thu, 29 Mar 2007 22:40:34 -0700, Walter Bright wrote:

> kris wrote:
>>    Cout.opCall("Hello, ").opCall(Cin.get);
> 
> Given:
> 	a.f(b,c);
> can be equivalently written as:
> 	f(a,b,c);
> 
> we can transform:
> 	Cout.opCall("Hello, ").opCall(Cin.get);
> into:
> 	opCall(opCall(Cout,"Hello, "), Cin.get);
> 
> And it is transformed that way, because the optimizer/back end knows nothing about member functions. They're just more function calls.
>
> The nested opCall will get called before the outer opCall, but otherwise the order is undefined (and will change depending on the function calling convention, whether it is a virtual call or not, etc.).

Now that makes sense. I can see now why the W&A show is concerned. In fact, the call-chaining example seems to flesh out to ...

   opCall(opCall(Cout,"Hello, "), get(Cin));

which means that either the 'get' could be called first or the inner 'opCall' could be called first. One can never be sure.

This would mean that the sequence example given by Kris ...

    Cout ("Please enter your name: ").flush;
    Cout ("Hello, ") (Cin.get);

gets transformed in to

    flush( opCall( Cout, "Please enter your name: "));
    opCall( opCall( Cout, "Hello, "), get(Cin));

The first statement will end up displaying the text on the console, leaving the cursor to the right of the ':' character. Now if the 'get' is called next, the user types in her name, say "mavis" and the screen will show ...

  Please enter your name: mavis
  _

and then the inner 'opCall' is run which will show (assuming a flush) ...

  Please enter your name: mavis
  Hello, mavis
  _


However, if the inner 'opCall' is called first the screen will show ...

  Please enter your name: Hello, _

and the user types in her name and we get ...

  Please enter your name: Hello, mavis
  mavis
  _


So the order is reasonable important to sort out.

Being the conservative procedural type of coder, I would have probably have coded ...

    char[] userName;
    Cout ("Please enter your name: ").flush;
    userName = Cin.get();
    Cout ("Hello, ");
    Cout (userName);
    Cout.flush;

Actually, now that I think about it, I'd really do ...

    char[] userName;
    char[] myResponse;

    Cout ("Please enter your name: ");
    Cout.flush;
    userName = Cin.get();
    myResponse = std.string.format("Hello, %s\n", userName);
    Cout.( myResponse );
    Cout.flush;

That is of course, if I'd really used stdin/stdout in that manner at all <G>

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Justice for David Hicks!"
30/03/2007 3:57:25 PM
March 30, 2007
On Thu, 29 Mar 2007 23:14:09 -0700, kris wrote:


> Thank you for that clarification, Walter.
> 
> You note that the inner opCall will be evaluated before the outer one; how is x.one(a).two(b).three(c) tranformed?

I'm guessing ...

   three( two( one(x, a), b), c);

thus either 'x' or 'a' is done first, then the other
 then either 'one' or 'b', then the other
 then either 'two' or 'c', then the other
 and finally 'three'

>> I suggest that at a minimum, if you wish to retain the current design, build a test into the test suite that verifies the required order of evaluation.
>> 
>> It's possible that in the future, to ensure source code portability of D, that the order of evaluation will become fixed. Such a change is a fair amount of work to accomplish, and will incur a runtime penalty (the implementation defined order allows the compiler to rearrange things to minimize register pressure). Even if the order was fixed, it still might not be in the right order for call chaining to work as your design needs it to.
> 
> Do you mean in terms of generic call-chaining, or that example specifically? For example, will the code x.one().two().three() always execute one() before two() ?

I think it would. But in the case of  "x.one(a).two(b)" then you can't be
sure if 'one' or 'b' will be evaluated first.

>> I also suggest that, with the maturing of the variadic template capability of D, using it will side step the order of evaluation problem completely. It'll still be an aesthetically pleasing design to the user.
> 
> That's certainly a possibility, Walter, but call-chaining is surely expected to operate in an orderly fashion regardless of whether it is used as an IO interface or not? For example, what happens if two() were to return a different object for three() to be applied against? It would surely be incorrect to generate code whereby three() were called against x instead? In other words, for call chaining to operate correctly the following pseudocode should likely hold true:
> 
> # auto result = x.one().two().three();
> 
> auto tmp1 = x.one();
> auto tmp2 = tmp1.two();
> auto result = tmp2.three();
> 
> Is that incorrect?

I think Walter is saying that a test similar to ...

  auto tmpA = x.one(a).two(b).three(c);
  auto tmp1 = x.one(a);
  auto tmp2 = tmp1.two(b);
  auto tmp3 = tmp2.three(c);
  assert( tmpA is tmp3);

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Justice for David Hicks!"
30/03/2007 4:25:04 PM
March 30, 2007
James Dennett wrote:
> kris wrote:
> 
>>James Dennett wrote:
>>
>>>kris wrote:
>>>
>>>
>>>>James Dennett wrote:
>>>>
>>>>
>>>>>kris wrote:
>>>>
>>>>[snip]
>>>>
>>>>
>>>>>>There never was any argument of which you claim. I simply noted that
>>>>>>eval-order had been clarified before, using your usage of "eval-order"
>>>>>>from within the same post. If you revisit, you'll see that was
>>>>>>actually
>>>>>>referring to call-chaining instead, so there's perhaps a misuse of
>>>>>>terms:
>>>>>>
>>>>>> Cout.opCall("Hello, ").opCall(Cin.get);
>>>>>>
>>>>>>As you can see, there is only one parameter passed to each call, and
>>>>>>therefore the order of /parameter/ eval is "not at stake here" (as I
>>>>>>noted to Frits). 
>>>>>
>>>>>
>>>>>There are two arguments to the second opCall.  One is
>>>>>the result of Cout.opCall("Hello, ") and the other is
>>>>>the result of Cin.get, and they can be evaluated in
>>>>>either order unless some rule prohibits it.
>>>>
>>>>I think you'll find that call-chaining does not operate in that manner,
>>>>James? If you look a bit closer, you'll see that the lhs has to be
>>>>evaluated first, simply to get something to deref the rhs. Further,
>>>>there is only one argument permitted to the opCall() itself
>>>>
>>>>For a fuller description, I suggest you bring it up with Walter instead?
>>>
>>>
>>>Walter's post in this thread (<eui80b$19hl$1@digitalmars.com>)
>>>seems to confirm my viewpoint as quoted above.  If you still
>>>don't think so after reading his message, I'd be interested
>>>if you can explain where Walter's explanation differs from
>>>mine.
>>>
>>>Regards,
>>>
>>>James.
>>
>>Simply because that's what Walter led us to believe a long time ago,
>>James, and it is how the compiler is implemented. Don't know what else
>>to tell you.
> 
> 
> I doubt that Walter lead you to believe that the order of
> evaluation was defined for the particular example code
> under discussion here.  ("Call chaining" is not the issue
> at hand; order of evaluation of function arguments is the
> relevant issue.)
> 

*sigh*

let's tease a couple of things apart, James? First, there is the question of call chaining operating in an ordered fashion. Second, there's the question of whether the miserable example is "correct" or not. Would you agree?


> Did you read the message of Walter's to which I referred?

Naturally


> 
> Walter wrote, today:
> 
> 
>>The nested opCall will get called before the outer opCall,
>>but otherwise the order is undefined (and will change
>>depending on the function calling convention, whether it
>>is a virtual call or not, etc.).
> 
> 
> This is very concrete and very specific, and supports what
> I wrote above.  Are you suggesting that you disagree with
> this?  (If so, with what do you disagree: that Walter wrote
> this, that it supports my viewpoint, or something else?)

You're tying two concerns together. Walter notes that the nested opCall is always called before the outer. This is consistent with the requirements for call-chaining (they should be called left to right). The only question remaining is that of what happens with three chained calls. I'm awaiting the answer from Walter, but I would certainly hope that call-chaining invocation order is maintained.

As for that sordid little example, it has generated way to much concern for something that should be merely of passing interest. It just happens to work because of output buffering, and no other reason.

In tango, the console is buffered and, in general, one has to flush the output before it will be emitted (just like a file). Console output has historically had a few 'shortcuts' added such as a newline adding a hidden .flush for interactive console usage. There is no newline in this daft example, and the only .flush present is on the first line. Thus, the example is relying on cleanup-code to flush the output buffers on its behalf. As I've noted before, this hardly exhibits good coding practice.

To make it perfectly clear, there was *never* any claim or belief that the Cin.get is or was evaluated before the Cout("hello "). We all intuitively *know* that the "hello " should be emitted before the Cin.get, but it isn't in that example. Again, it is because of buffering and the complete lack of an intervening flush on the console output.

There's a good reason why "mystery" is in the topic line ;)

I do hope that helps?