March 29, 2007
Walter Bright wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> As long as it's not in the language definition, you can't count on it. I think this is a language defect anyhow; I am lobbying Walter to define left-to-right order of evaluation in all cases.
> 
> It's problematical to define the order of function argument evaluation, because some calling conventions push left-to-right, others right-to-left.
> 
> Not impossible, though.

It sounds like parameter evaluation order is being confused with evaluation order in expressions?  Or is Andrei truly suggesting that function arguments should be evaluated left to right?


Sean
March 29, 2007
Sean Kelly wrote:
> Walter Bright wrote:
>> Andrei Alexandrescu (See Website For Email) wrote:
>>> As long as it's not in the language definition, you can't count on it. I think this is a language defect anyhow; I am lobbying Walter to define left-to-right order of evaluation in all cases.
>>
>> It's problematical to define the order of function argument evaluation, because some calling conventions push left-to-right, others right-to-left.
>>
>> Not impossible, though.
> 
> It sounds like parameter evaluation order is being confused with evaluation order in expressions?  Or is Andrei truly suggesting that function arguments should be evaluated left to right?

Let's say you have:

	a.f(b,c);

'a' can be semantically a function argument, as in

	f(a,b,c);

Now, 'a' must be evaluated before f can be called, but whether a, b or c are evaluated first is not defined.
March 29, 2007
Walter Bright wrote:
> Sean Kelly wrote:
>> Walter Bright wrote:
>>> Andrei Alexandrescu (See Website For Email) wrote:
>>>> As long as it's not in the language definition, you can't count on it. I think this is a language defect anyhow; I am lobbying Walter to define left-to-right order of evaluation in all cases.
>>>
>>> It's problematical to define the order of function argument evaluation, because some calling conventions push left-to-right, others right-to-left.
>>>
>>> Not impossible, though.
>>
>> It sounds like parameter evaluation order is being confused with evaluation order in expressions?  Or is Andrei truly suggesting that function arguments should be evaluated left to right?
> 
> Let's say you have:
> 
>     a.f(b,c);
> 
> 'a' can be semantically a function argument, as in
> 
>     f(a,b,c);
> 
> Now, 'a' must be evaluated before f can be called, but whether a, b or c are evaluated first is not defined.

Okay, right.  This is what I'd expect.  Though I suppose it could indeed be somewhat confusing with property syntax above.  Personally though, as long as this is well documented, I don't see a real need to change it?


Sean
March 29, 2007
Sean Kelly wrote:
>> Let's say you have:
>>
>>     a.f(b,c);
>>
>> 'a' can be semantically a function argument, as in
>>
>>     f(a,b,c);
>>
>> Now, 'a' must be evaluated before f can be called, but whether a, b or c are evaluated first is not defined.
> 
> Okay, right.  This is what I'd expect.  Though I suppose it could indeed be somewhat confusing with property syntax above.  Personally though, as long as this is well documented, I don't see a real need to change it?

One reason to disallow implementation flexibility on this is to improve source code portability.
March 30, 2007
Sean Kelly wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>>
>> So what's the recommended use of Cout?
>>
>> (a) If you do stdio, use Cout (and don't forget to flush Cout manually every time you plan to read from Cin).
> 
> Oh right, this is what tie() is for with iostreams.  It's come up before in the past, though I can't recall if any conclusions were reached. I'll admit that the current behavior is a tad confusing here though.
> 
>> (b) But not for multithreaded programs that do stdio. For those, use the logger facility. If you want multithreaded output to stdout, copy the code from http://www.dsource.org/projects/tango/wiki/ChapterLogging into your program. Be careful that that code might be incorrect; the manual doesn't specify. If it is correct, be careful with what you do inside that code, because you could deadlock yourself.
> 
> To be fair, this is mostly a documentation issue.  Though it may be that the logger could be made easier to use for your example.  I'll have to look at the logger in more detail before I say any more--I've only used it a handful of times.
> 
>> (c) And not for programs linking with anything that uses C's stdio. For those, use Phobos.
> 
> Perhaps more accurately "programs that expect to mix output to the same device as linked libraries using C's stdio."  I can't recall ever needing this option, YMMV (and it obviously does).  One option might be to create a CStdioCondiuit or some such that uses C library calls instead of platform calls for IO.  Then the user could choose the appropriate one.
> 
> I think it's also perhaps worth noting that Tango is truly intended to be as much of an application framework as it is a collection of useful tools.  In fact, some non-essential features have been deliberately excluded to avoid discouraging third-party development.  I would also personally consider C stdio integration to be a specialized need for the typical D programmer, and therefore it is arguably not appropriate as the default configuration for Tango's IO layer if doing so incurs design or performance penalties (both of which seem to be the case, based on recent experimentation).  Therefore, I am not convinced that C integrated IO should be the default behavior for Tango's Cin/Cout, and feel it may be better offered as a separate conduit or handled some other way.  My opinion may well change if this proves to be a real issue for many people, but that's my gut feeling at the moment.

I understand. Probably making the mode fast/incompatible by default is justifiable if people who need compatibility can opt-out and write one line of code (but not more!) to funnel things through the std handles henceforth. That is an opt-out scheme, and it looks like a decent solution.

The two other things that would be worth looking into are (a) allowing Cout(a, b) as a replacement for Cout(a)(b) and phasing the latter out, and (b) having Cin automatically call Cout.flush under certain conditions (probably a good heuristics: both are TTYs).


Andrei
March 30, 2007
Sean Kelly wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
> 
>>
>> So what's the recommended use of Cout?
>>
>> (a) If you do stdio, use Cout (and don't forget to flush Cout manually every time you plan to read from Cin).
> 
> 
> Oh right, this is what tie() is for with iostreams.  It's come up before in the past, though I can't recall if any conclusions were reached. I'll admit that the current behavior is a tad confusing here though.

I'm not entirely sure what causes the sky to fall down for Andrei here? Apparently there's some kind of crisis due to a need to flush a "prompt" to the console before reading some related input? e.g. there's anguish over

  Cout ("name? ").flush;
  Cin.get();

Sure, a little sugar in the form of tie() might be nice, but there's nothing fundamentally wrong with the current approach as it is, IMO. Anything else is just gravy. I sure hope there ain't no crisis here ;)

If I've got this wrong, Sean, I'd appreciate clarification?



>> (b) But not for multithreaded programs that do stdio. For those, use the logger facility. If you want multithreaded output to stdout, copy the code from http://www.dsource.org/projects/tango/wiki/ChapterLogging into your program. Be careful that that code might be incorrect; the manual doesn't specify. If it is correct, be careful with what you do inside that code, because you could deadlock yourself.
> 
> 
> To be fair, this is mostly a documentation issue.  Though it may be that the logger could be made easier to use for your example.  I'll have to look at the logger in more detail before I say any more--I've only used it a handful of times.

From what I recall, logging is expected to be used by engineers: that is, if you write yourself a stupid plug-in for the logging framework, then you have no-one but yourself to blame. Similar silliness could be applied to, say, the GC ~ "OMG! I can write a dtor that borks the GC when it calls me!" ... this is the playground of children and fools alone.

On the other hand, using the logger is not a requirment. While it exposes a powerful tool, a lot of folk will simply never see the value in it, or find other (perhaps valid) reason to not use it. People, in general, don't much care for change; they'll fight it tooth and nail, especially those who are used to getting their own way all the time.

As far as atomic console output is concerned, I feel it's a little like the arguments over Container api's being synchronized or not. There are valid cases where such apis can show benefit when synchronized, but most people realize that the common tradeoffs are not particularly great.

I see similar concerns with console usage: for those cases that actually need atomic console ouput, it's certinaly possible to write a trivial wrapper around it and synchronize that instead (it might even wind up in the library). However, it seems wholly at odds to have individuals howling for crazy-shizzle throughput on one hand, and at the same time condemning a lack of atomicity for a select number of cases that /might/ use it ;)

There's also the issue of multi-component atomicity, where you need a series of constructs sent to some output as a single entity, protected from the whims of multiple threads. An atomic console does nothing much to help in this more general case, in the same way that multiple threads writing to the same file are not co-ordinated across multiple calls. The issue is a larger one than might meet the eye, and band-aids are not an appropriate solution IMO. Would be interested if folks had some ideas about this in the larger scale, but on a different thread ... for example, D has these nice delegates with lazy expressions ~ something like that might be used for such purposes?

Anyway; seems like the poor old console has to do quad-duty, while all the other available tools go on vacation. This is not an appropriate strategy in general, and I'm not super-enthuisiastic about encouraging people to use Cout to chop wood.
March 30, 2007
kris wrote:
> Sean Kelly wrote:
>> Andrei Alexandrescu (See Website For Email) wrote:
>>
>>>
>>> So what's the recommended use of Cout?
>>>
>>> (a) If you do stdio, use Cout (and don't forget to flush Cout manually every time you plan to read from Cin).
>>
>>
>> Oh right, this is what tie() is for with iostreams.  It's come up before in the past, though I can't recall if any conclusions were reached. I'll admit that the current behavior is a tad confusing here though.
> 
> I'm not entirely sure what causes the sky to fall down for Andrei here? Apparently there's some kind of crisis due to a need to flush a "prompt" to the console before reading some related input? e.g. there's anguish over
> 
>   Cout ("name? ").flush;
>   Cin.get();
> 
> Sure, a little sugar in the form of tie() might be nice, but there's nothing fundamentally wrong with the current approach as it is, IMO. Anything else is just gravy. I sure hope there ain't no crisis here ;)
> 
> If I've got this wrong, Sean, I'd appreciate clarification?

No, that's exactly right.  And I think the current need for a .flush is consistent with how Tango IO behaves everywhere.  I suppose it would be nice if the feature were offered (assuming there is an elegant way to provide it), but the decision for whether or not to enable it by default for Cin/Cout is a separate issue.


Sean
March 30, 2007
On Thu, 29 Mar 2007 13:39:49 -0700, kris wrote:

> If I recall correctly, it started off as a complaint about the use of call-chaining in Mango, and resulted in someone leaving the NG for good (was his name manfred or something?)


Oh, Manfred is back... he certainly didn't leave for good; he just appears to have been on a sabbatical. :)

I do remember those long debates on the issue. :(

-JJR
March 30, 2007
Sean Kelly wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> kris wrote:
>>> Sean Kelly wrote:
>>> [snip]
>>>> I must be missing something.  Why is the following not acceptable?
>>>>
>>>>     import tango.io.Console;
>>>>
>>>>     void main()
>>>>     {
>>>>         char[] name;
>>>>         Cout( "Please enter your name: " ).flush;
>>>>         Cin.nextLine( name );
>>>>         Cout( "Hello, " )( name )( "!" );
>>>>     }
>>>
>>>
>>> There used to be a tango/example like this variation:
>>>
>>>     import tango.io.Console;
>>>
>>>     void main()
>>>     {
>>>         Cout ("Please enter your name: ").flush;
>>>         Cout ("Hello, ") (Cin.get);
>>>     }
>>
>> Ah, also, the last line is translated into:
>>
>> Cout.opCall("Hello, ").opCall(Cin.get);
>>
>> D does not specify evaluation order, so the code might end up printing "Hello, " before reading the standard input.
> 
> We discussed this a long time ago and came to the conclusion that while the D spec does not guarantee evaluation order for this scenario, it seems impossible for it to be anything other than left to right because the chained calls rely on the return value from previous calls.  If this is untrue then I'd love to know the reasoning.

All you get from that is a partial order that says
that the arguments to a function must be evaluated
before that function is called.  There's nothing
to say which order they're evaluated in; Cin.get
can be called at any time before the opCall that
uses its result, including being the first call in
the statement.  There's no requirement for anything
to happen before Cin.get is called.

-- James
March 30, 2007
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