www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - stdio line-streaming revisited

reply kris <foo bar.com> writes:
Last week there were a series of posts regarding some optimized code 
within phobos streams. A question posed was, without those same 
optimizations, would tango.io be slower than the improved phobos [1]

As these new phobos IO functions are now available, Andrei's "benchmark" 
[2] was run on both Win32 and linux to see where tango.io could use some 
improvement.

The results indicate:

1) on linux, the fastest variation of the revised phobos code runs 40% 
slower than the generic tango.io equivalent. On the other hand, the new 
phobos code seems a bit faster than perl

2) on win32, similar testing shows tango.io to be more than six times 
faster than the improved phobos code. Tweaking the tango.io library a 
little makes it over eight times faster than the phobos equivalent [3]

3) On Win32, generic tango.io is more than twice as efficient as the 
fastest C version identified. It's also notably faster than MinGW 'cat', 
which apparently performs various under-the-cover optimizations.

4) by making some further optimizations in the phobos client-code using 
setvbuf() and fputs(), the improved phobos version can be sped up 
significantly; at that point tango.io is only three times faster than 
phobos on Win32. These adjustments require knowledge of tweaking the 
underlying C library; thus, they may belong to the group of C++ tweaks 
which Walter quibbled with last week. The setvbuf() tweaks make no 
noticable difference on linux, though the fputs() improvements are 
accounted for in #1 (above)


Note that tango.io is not explicitly optimized for this behaviour. While 
some quick hacks to the library have been shown to make it around 20% 
faster than the generic package (for this specfic test), the efficiency 
benefits are apparently derived through the approach more than anything 
else. With some changes to a core tango.io module, similar performance 
multipliers could presumeably be exhibited on linux platforms also. That 
is: tango.io is relatively sedate on linux, compared to its win32 variation.

FWIW: if some of those "Language Shootout" tests are IO-bound, perhaps 
tango.io might help? Can't imagine they'd apply that as a "language" 
test, but stranger things have happened before.


Here's the tango.io client (same as last week):

-------------
import tango.io.Console;

void main()
{
   char[] content;

   while (Cin.nextLine (content, true))
          Cout (content);
}
------------


and here's the fastest phobos equivalent. Removing the setvbuf() code 
makes it consume around twice as much time on Win32. Note that this 
version is faster than the equivalent code posted last week, though 
obviously more specialized and verbose:

------------
import std.stdio;
import std.cstream;

void main() {
     char[] buf = new char[1000 ];
     size_t len;
     const size_t BUFSIZE = 2 * 1024;

     setvbuf(stdin, null, _IOFBF, BUFSIZE);
     setvbuf(stdout, null, _IOFBF, BUFSIZE);

     while (( len = readln(buf)) != 0) {
         assert(len < 1000);
         buf[len] = '\0';
         fputs(buf.ptr, stdout);
     }
}
------------


[1] Timing measurements can be supplied to those interested.

[2] The recent changes within phobos apparently stemmed from Andrei 
piping large text files through his code, and this "benchmark" is a 
reflection of that process.

[3] That ~20% optimization has been removed from the generic package at 
this time, since we feel it doesn't contribute very much to the overall 
IO picture. It can be restored if people find that necessary, and there 
is no change to client code.
Mar 28 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Last week there were a series of posts regarding some optimized code 
 within phobos streams. A question posed was, without those same 
 optimizations, would tango.io be slower than the improved phobos [1]
 
 As these new phobos IO functions are now available, Andrei's "benchmark" 
 [2] was run on both Win32 and linux to see where tango.io could use some 
 improvement.

On my machine, Tango does 4.3 seconds and the following phobos program (with Walter's readln) does 5.4 seconds: #!/usr/bin/env rundmd import std.stdio; void main() { char[] line; while (readln(line)) { write(line); } } where write is a function that isn't yet in phobos, of the following implementation: size_t write(char[] s) { return fwrite(s.ptr, 1, s.length, stdout); } Also, the Tango version has a bug. Running Tango's cat without any pipes does not read lines from the console and outputs them one by one, as it should; instead, it reads many lines and buffers them internally, echoing them only after the user has pressed end-of-file (^D on Linux), or possibly after the user has entered a large amount of data (I didn't have the patience). The system cat program and the phobos implementation correctly process each line as it was entered. This bug should be fixed for the programs to be comparable. After that, it would help giving numbers comparing all of tango, phobos, and cat, with the perl baseline. Andrei
Mar 28 2007
next sibling parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 
 Last week there were a series of posts regarding some optimized code 
 within phobos streams. A question posed was, without those same 
 optimizations, would tango.io be slower than the improved phobos [1]

 As these new phobos IO functions are now available, Andrei's 
 "benchmark" [2] was run on both Win32 and linux to see where tango.io 
 could use some improvement.

[snip] On my machine, Tango does 4.3 seconds and the following phobos program (with Walter's readln) does 5.4 seconds:

On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.
 
 #!/usr/bin/env rundmd
 import std.stdio;
 
 void main() {
   char[] line;
   while (readln(line)) {
     write(line);
   }
 }
 
 where write is a function that isn't yet in phobos, of the following 
 implementation:
 
 size_t write(char[] s) {
   return fwrite(s.ptr, 1, s.length, stdout);
 }

Wondered where that had gone
 
 Also, the Tango version has a bug. Running Tango's cat without any pipes 
 does not read lines from the console and outputs them one by one, as it 
 should; instead, it reads many lines and buffers them internally, 
 echoing them only after the user has pressed end-of-file (^D on Linux), 
 or possibly after the user has entered a large amount of data (I didn't 
 have the patience). The system cat program and the phobos implementation 
 correctly process each line as it was entered.

If you mean something that you've written, that could presumeably be rectified by adding the isatty() test Walter had mentioned before. That has not been added to tango.io since (a) it would likely make programs behave differently depending on whether they were redirected or not. It's not yet clear whether that is an appropriate specialization, as default behaviour, and (b) there has been no ticket issued for it Again, please submit a ticket so we don't forget about that detail. We'd be interested to hear if folk think the "isatty() test" should be default behaviour, or would perhaps lead to corner-case issues instead
Mar 28 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:

 Last week there were a series of posts regarding some optimized code 
 within phobos streams. A question posed was, without those same 
 optimizations, would tango.io be slower than the improved phobos [1]

 As these new phobos IO functions are now available, Andrei's 
 "benchmark" [2] was run on both Win32 and linux to see where tango.io 
 could use some improvement.

[snip] On my machine, Tango does 4.3 seconds and the following phobos program (with Walter's readln) does 5.4 seconds:

On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.

If I understand things correctly, it looks like the hope is to derive more speed from further dropping phobos and C I/O compatibility, a path that I personally don't consider attractive. Also, the fact that the tango version is "more than twice as efficient as the fastest C version identified" suggests a problem with the testing method or with the C code. Are they comparable? If you genuinely have a method to push bits through two times faster than the fastest C can do, you may want as well go ahead and patent it. Your method would speed up many programs, since many use C's I/O and are I/O bound. It's huge news. I'm not even kidding. But I doubt that that's the case.
 Also, the Tango version has a bug. Running Tango's cat without any 
 pipes does not read lines from the console and outputs them one by 
 one, as it should; instead, it reads many lines and buffers them 
 internally, echoing them only after the user has pressed end-of-file 
 (^D on Linux), or possibly after the user has entered a large amount 
 of data (I didn't have the patience). The system cat program and the 
 phobos implementation correctly process each line as it was entered.

If you mean something that you've written, that could presumeably be rectified by adding the isatty() test Walter had mentioned before. That has not been added to tango.io since (a) it would likely make programs behave differently depending on whether they were redirected or not. It's not yet clear whether that is an appropriate specialization, as default behaviour

What is absolutely clear is that the current version has a bug. It can't read a line from the user and write it back. There cannot be any question that that's a problem.
, and (b) there has been no ticket issued for it
 
 Again, please submit a ticket so we don't forget about that detail. We'd 
 be interested to hear if folk think the "isatty() test" should be 
 default behaviour, or would perhaps lead to corner-case issues instead

I was actually pointing out a larger issue: incompatibility with phobos' I/O and C I/O. Tango's version is now faster (thank God we got past the \n issue and bummer it's not the default parameter of nextLine) but it is incompatible with both phobos' and C's stdio. (It's possible that the extra speed is derived from skipping C's stdio and using read and write directly.) Probably you could reimplement phobos and bundle it with Tango to give the users the option to link phobos code with Tango code properly, but still C stdio compatibility is lost, and phobos code has access to it. Andrei
Mar 28 2007
parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 
 Andrei Alexandrescu (See Website For Email) wrote:

 kris wrote:

 Last week there were a series of posts regarding some optimized code 
 within phobos streams. A question posed was, without those same 
 optimizations, would tango.io be slower than the improved phobos [1]

 As these new phobos IO functions are now available, Andrei's 
 "benchmark" [2] was run on both Win32 and linux to see where 
 tango.io could use some improvement.

[snip] On my machine, Tango does 4.3 seconds and the following phobos program (with Walter's readln) does 5.4 seconds:

On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.

If I understand things correctly, it looks like the hope is to derive more speed from further dropping phobos and C I/O compatibility, a path that I personally don't consider attractive.

Nope. That's not the case at all. The expectation (or 'hope', if you like) is that we can make the linux version operate more like the Win32 version
 
 Also, the fact that the tango version is "more than twice as efficient 
 as the fastest C version identified" suggests a problem with the testing 
 method or with the C code. Are they comparable? If you genuinely have a 
 method to push bits through two times faster than the fastest C can do, 
 you may want as well go ahead and patent it. Your method would speed up 
 many programs, since many use C's I/O and are I/O bound. It's huge news. 

That's good for D then? There's no reason why C could not take the same approach yet, one might imagine, the IO strategies exposed and the wide variety of special cases may 'discourage' the implementation of a more efficient approach? That's just pure speculation on my part, and I'm quite positive the C version could be sped up notably if one reimplemented a bunch of things.
 I'm not even kidding. But I doubt that that's the case.

You're most welcome to your doubts, Andrei. However, just because "C does it that way" doesn't mean it is, or ever was, the "best" approach
 
 Also, the Tango version has a bug. Running Tango's cat without any 
 pipes does not read lines from the console and outputs them one by 
 one, as it should; instead, it reads many lines and buffers them 
 internally, echoing them only after the user has pressed end-of-file 
 (^D on Linux), or possibly after the user has entered a large amount 
 of data (I didn't have the patience). The system cat program and the 
 phobos implementation correctly process each line as it was entered.

If you mean something that you've written, that could presumeably be rectified by adding the isatty() test Walter had mentioned before. That has not been added to tango.io since (a) it would likely make programs behave differently depending on whether they were redirected or not. It's not yet clear whether that is an appropriate specialization, as default behaviour

What is absolutely clear is that the current version has a bug. It can't read a line from the user and write it back. There cannot be any question that that's a problem.

Only with the way that you've written your program. In the general case, that is not true at all. But please do submit that bug-report :)
 
 , and (b) there has been no ticket issued for it

 Again, please submit a ticket so we don't forget about that detail. 
 We'd be interested to hear if folk think the "isatty() test" should be 
 default behaviour, or would perhaps lead to corner-case issues instead

I was actually pointing out a larger issue: incompatibility with phobos' I/O and C I/O. Tango's version is now faster (thank God we got past the \n issue and bummer it's not the default parameter of nextLine) but it is incompatible with both phobos' and C's stdio. (It's possible that the extra speed is derived from skipping C's stdio and using read and write directly.) Probably you could reimplement phobos and bundle it with Tango to give the users the option to link phobos code with Tango code properly, but still C stdio compatibility is lost, and phobos code has access to it.

The issue you raise here is that of interleaved and shared access to global entities, such as the console, where some incompatability between tango.io and C IO is exhibited. If you really dig into it, you'll perhaps conclude that (a) the number of real-world scenario where this would truly become an issue is diminishingly small, and (b) the vast (certainly on Win32) performance improvement is worth that tradeoff. Even then, it is certainly possible to intercept C IO functions and route them to tango.io equivalents instead. It has been said before, but is probably worth repeating: - Tango is not a phobos clone. Nor is it explicitly designed to be compatible with phobos; sometimes it is worthwhile taking a different approach. Turns out that phobos can be run alongside tango in many situations. - Tango is for D programmers; not C programmers. - Tango, as a rule, is intended to be flexible, modular, efficient and practical. The goal is to provide D with an exceptional library, and we reserve the right to break a few eggs along the way ;)
Mar 28 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:

 - Tango is for D programmers; not C programmers.

D programmers sometimes like to call 3rd party code written in other languages, and pretty much any interop in D has to happen via C compatibility. E.g. pyD. So I'm guessing if my D code calls on some Python code that prints to the console that somewhere down the line that eventually ends up on C's stdout. I could be wrong, but at least that's why I *think* Andrei and Walter keep saying that C compatibility is important. Andrei -- by "compatibility" does that mean if I rebind stdio/stdout to something different that both D and C's output go to the new place? Or is it still necessary to rebind them individually? I did this once for some legacy code in C++, and found that I had to rebind 3 things: the C streams, the C++ old-style streams from <iostream.h> (was under MSVC 6), and the new-style C++ streams from <iostream>. And then I had to do the interleaving myself, which didn't really work (because all the streams were just writing to output buffers individually). If what you're talking about with compatibility would avoid that kind mess, that is certainly be a good thing. --bb
Mar 28 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:

 - Tango is for D programmers; not C programmers.

D programmers sometimes like to call 3rd party code written in other languages, and pretty much any interop in D has to happen via C compatibility. E.g. pyD. So I'm guessing if my D code calls on some Python code that prints to the console that somewhere down the line that eventually ends up on C's stdout. I could be wrong, but at least that's why I *think* Andrei and Walter keep saying that C compatibility is important.

Yes.
 Andrei -- by "compatibility" does that mean if I rebind stdio/stdout to 
 something different that both D and C's output go to the new place?  Or 
 is it still necessary to rebind them individually?  I did this once for 
 some legacy code in C++, and found that I had to rebind 3 things: the C 
 streams, the C++ old-style streams from <iostream.h> (was under MSVC 6), 
 and the new-style C++ streams from <iostream>.    And then I had to do 
 the interleaving myself, which didn't really work (because all the 
 streams were just writing to output buffers individually).  If what 
 you're talking about with compatibility would avoid that kind mess, that 
 is certainly be a good thing.

Exactly. I'm hoping to save the D community from the iostreams mess, which requires the programmer to deal with two (or three as in your example) incompatible APIs. It's iostreams' amazing counterperformance that they managed to cut the speed so effectively in the process :o). I think a wise thing that Tango could do is to offer a backward-compatible stdio, and a high-performance I/O class using the latest and greatest from each I/O (zero-copy (see http://research.sun.com/techrep/1995/abstract-39.html), asynchronous, threaded, kernelized, teleported, you name it). The main desideratum is that fast/incompatible I/O is opt-in, not lock-in. Andrei
Mar 28 2007
next sibling parent kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
[snip]
 Exactly. I'm hoping to save the D community from the iostreams mess, 
 which requires the programmer to deal with two (or three as in your 
 example) incompatible APIs. 

Save the D community? The sky is not falling; no need for you to save anyone from anything
 It's iostreams' amazing counterperformance 
 that they managed to cut the speed so effectively in the process :o).
 
 I think a wise thing that Tango could do is to offer a 
 backward-compatible stdio, and a high-performance I/O class using the 
 latest and greatest from each I/O (zero-copy (see 
 http://research.sun.com/techrep/1995/abstract-39.html), asynchronous, 
 threaded, kernelized, teleported, you name it).
 
 The main desideratum is that fast/incompatible I/O is opt-in, not lock-in.

If you wish to be helpful, then let's try to forego the overt drama? Here's an simple exercise you might indulge; let's do it in steps: 1) name every single C library that D programmers regularly use from D
Mar 29 2007
prev sibling parent reply Dave <Dave_member pathlink.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:

 - Tango is for D programmers; not C programmers.

D programmers sometimes like to call 3rd party code written in other languages, and pretty much


on some Python code that prints to the console that somewhere down the line that eventually ends up on C's stdout. I could be wrong, but at least Well, it *all* ends up using file descriptor 0 on POSIX systems.
 that's why I *think* Andrei and Walter keep saying that C compatibility is
important.

Yes.

Please help me to understand this issue... C's stdio predefined set of file handles or streams are simply wrappers around "pipes" into and out of the console, which in itself is just a part of another separate program (the shell). For the _great majority_ of cases where a D program interacts with another program though a shell (which it will even if spawned via the POSIX exec() family for example), as long as Tango can read from and write -- and eventually flush if/as the underlying shell requires -- to the console, why would a D program really need to read and write to the exact same handles? The same *file descriptors as the shell, yes,* but file handles? Basically what I'm saying is this: Let the shell handle this stuff as it sees fit; that's what it is there for! For more complex high-throughput (http://cr.yp.to/qmail.html) or multi-threaded C programs that could use the stdio API, you almost have to use a closer to the metal API (like unistd read()/write()) anyway. Somewhere in the docs for Qmail, security and performance issues were the main reason why it was written with a complete replacement for stdio. IIRC, that developer really rails on C's stdio as outdated, and as something that drastically needs to be replaced. BTW, I myself have written programs that interact very well (and easily) with Qmail though C's stdio even though the only common bond is the POSIX file descriptors. And for the minority interop case, as long as Tango I/O offers the flexibility to follow the same conventions as the client or server process regardless of shell conventions, I would think that would be good enough, as this code often needs to be "hand-crafted" -- at least in my experience --, or use the shell native API anyway (i.e.: read()/write()). I don't know much about Tango, but it actually looks like it offers much broader functionality than phobos in this regard (for example, the http classes). Can I get a concrete and preferably common example of where the current Tango implementation fails for stdin/stdout/stderr in this regard? Tango does not fail with the benchmark/example you provided... Similar functionality (at its base) as your program is by far the most common use case for reading and writing to the console.
 Andrei -- by "compatibility" does that mean if I rebind stdio/stdout to
something different that 


did this once for some legacy code in C++, and found that I had to rebind 3 things: the C streams, the C++ old-style streams from <iostream.h> (was under MSVC 6), and the new-style C++ streams from <iostream>. And then I had to do the interleaving myself, which didn't really work (because all the streams were just writing to output buffers individually). If what you're talking about with compatibility would avoid that kind mess, that is certainly be a good thing.
 Exactly. I'm hoping to save the D community from the iostreams mess, which
requires the 

counterperformance that they managed to cut the speed so effectively in the process :o).
 I think a wise thing that Tango could do is to offer a backward-compatible
stdio, and a 

http://research.sun.com/techrep/1995/abstract-39.html), asynchronous, threaded, kernelized, teleported, you name it).

I really don't think it needs to be that complicated, especially since Tango apparently performs very well anyhow for the most common cases.
 The main desideratum is that fast/incompatible I/O is opt-in, not lock-in.

If phobos and Tango could be bundled together on equal footing with the D distributions, phobos could fill the 'stdio compatible' role. - Where that is needed (the minority of cases, almost always developed by experienced programmers), D can easily do that. Tango and/or phobos could even provide a separate API for that, but not the default. - The "C way" is not the "D way". D is supposed to be an improvement over C w/o all of the C++ corner cases. This paradigm should extend to the std. lib. as well. - A lot of very experienced and smart people have contributed to Tango. I'd venture a guess that as a group they have all used C's stdio extensively and have also done various forms of interop in the past. They apparently don't have a problem with the current implementation, so I'd be very reluctant to question them on it w/o some very concrete examples of the most common use cases that fail with Tango. - Speaking only for myself, at one time I was reluctant to accept anything like Tango simply because it seemed to be a split from Walter's direction, and because phobos was kind-of 'C like' so I was more comfortable with it. However, here is the Tango team willing and able to contribute, and more than happy (for the most part) to leave the compiler development and overall direction of the language to Walter. I think Walter's (and perhaps your?) time is best spent on that, and let the Tango team wrestle with the std. lib. I'd like to suggest this: Distribute both phobos and Tango on equal footing with the D distributions, and have the compiler link both in by default (with a switch to link only one): - Walter and David Friedman could automate the build process to grab the latest Tango. - Some overlapping areas (like the GC) would have to be resolved somehow (Tango team - how tough would this be?). - Add the Tango docs. to the DigitalMars site on "equal footing" as well. - C++ distributions provide somewhat of a template and precedence for distributing more than one library with the compiler. Thanks, - Dave
 Andrei

Apr 01 2007
parent Sean Kelly <sean f4.ca> writes:
Dave wrote:
 
 I'd like to suggest this: Distribute both phobos and Tango on equal 
 footing with the D distributions, and have the compiler link both in by 
 default (with a switch to link only one):
 
 - Walter and David Friedman could automate the build process to grab the 
 latest Tango.
 - Some overlapping areas (like the GC) would have to be resolved somehow 
 (Tango team - how tough would this be?).

The compiler runtime is a logically separate entity in Tango. Some cooperation would be necessary for the development of new back-end functionality, etc, but the compiler runtime could theoretically be maintained entirely separate from the rest of the library if Walter and/or David chose to do so. That said, updating the Tango runtime for new compiler releases is generally fairly straightforward, so a shift in responsibility would probably just change the update process rather than simplify it. Sean
Apr 01 2007
prev sibling next sibling parent kris <foo bar.com> writes:
Bill Baxter wrote:
 kris wrote:
 
 Andrei Alexandrescu (See Website For Email) wrote:

 - Tango is for D programmers; not C programmers.

D programmers sometimes like to call 3rd party code written in other languages, and pretty much any interop in D has to happen via C compatibility. E.g. pyD. So I'm guessing if my D code calls on some Python code that prints to the console that somewhere down the line that eventually ends up on C's stdout. I could be wrong, but at least that's why I *think* Andrei and Walter keep saying that C compatibility is important. Andrei -- by "compatibility" does that mean if I rebind stdio/stdout to something different that both D and C's output go to the new place? Or is it still necessary to rebind them individually? I did this once for some legacy code in C++, and found that I had to rebind 3 things: the C streams, the C++ old-style streams from <iostream.h> (was under MSVC 6), and the new-style C++ streams from <iostream>. And then I had to do the interleaving myself, which didn't really work (because all the streams were just writing to output buffers individually). If what you're talking about with compatibility would avoid that kind mess, that is certainly be a good thing. --bb

That post noted the feasibility of hooking C into tango.io, which is a potentially realistic option
Mar 28 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
John Reimer wrote:
 On Thu, 29 Mar 2007 15:19:40 +0900, Bill Baxter wrote:
 
 kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 - Tango is for D programmers; not C programmers.

languages, and pretty much any interop in D has to happen via C compatibility. E.g. pyD. So I'm guessing if my D code calls on some Python code that prints to the console that somewhere down the line that eventually ends up on C's stdout. I could be wrong, but at least that's why I *think* Andrei and Walter keep saying that C compatibility is important.

It's only important for those that are determined to use it, perhaps because of persistant programming style or unrelenting attachment to C++ methods (where C and C++ seemed to keep an unholy matrimony). I'd hate to see a D library infected with a C compatibility requirement -- support for ugly C design decisions would most certainly taint Tango for the worse. I believe Tango was made to help D step beyond that.

I believe the current discussion is only about under-the-hood implementation issues. So I don't think you have to worry about any D libraries exposing (good or bad) C/C++ design decisions to users. Tango is going to expose the same D interface no matter how it's implemented under the hood. --bb
Mar 29 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 John Reimer wrote:
 On Thu, 29 Mar 2007 15:19:40 +0900, Bill Baxter wrote:

 kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 - Tango is for D programmers; not C programmers.

languages, and pretty much any interop in D has to happen via C compatibility. E.g. pyD. So I'm guessing if my D code calls on some Python code that prints to the console that somewhere down the line that eventually ends up on C's stdout. I could be wrong, but at least that's why I *think* Andrei and Walter keep saying that C compatibility is important.

It's only important for those that are determined to use it, perhaps because of persistant programming style or unrelenting attachment to C++ methods (where C and C++ seemed to keep an unholy matrimony). I'd hate to see a D library infected with a C compatibility requirement -- support for ugly C design decisions would most certainly taint Tango for the worse. I believe Tango was made to help D step beyond that.

I believe the current discussion is only about under-the-hood implementation issues. So I don't think you have to worry about any D libraries exposing (good or bad) C/C++ design decisions to users. Tango is going to expose the same D interface no matter how it's implemented under the hood.

That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs. Andrei
Mar 29 2007
parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 I believe the current discussion is only about under-the-hood 
 implementation issues.  So I don't think you have to worry about any D 
 libraries exposing (good or bad) C/C++ design decisions to users.  
 Tango is going to expose the same D interface no matter how it's 
 implemented under the hood.

That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output? Sean
Mar 29 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 I believe the current discussion is only about under-the-hood 
 implementation issues.  So I don't think you have to worry about any 
 D libraries exposing (good or bad) C/C++ design decisions to users.  
 Tango is going to expose the same D interface no matter how it's 
 implemented under the hood.

That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output?

Cout. The thing is that the design using the syntax Cout(a)(b) is conducive to two separate calls to the library. I recall this has been briefly discussed here. Andrei
Mar 29 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 I believe the current discussion is only about under-the-hood 
 implementation issues.  So I don't think you have to worry about any 
 D libraries exposing (good or bad) C/C++ design decisions to users.  
 Tango is going to expose the same D interface no matter how it's 
 implemented under the hood.

That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output?

Cout. The thing is that the design using the syntax Cout(a)(b) is conducive to two separate calls to the library. I recall this has been briefly discussed here.

Of course that could easily be fixed if either struct destructors or scope class returning were to be added to the language. Then you could just return something with a destructor that releases the lock.
Mar 29 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 I believe the current discussion is only about under-the-hood 
 implementation issues.  So I don't think you have to worry about 
 any D libraries exposing (good or bad) C/C++ design decisions to 
 users.  Tango is going to expose the same D interface no matter how 
 it's implemented under the hood.

That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output?

Cout. The thing is that the design using the syntax Cout(a)(b) is conducive to two separate calls to the library. I recall this has been briefly discussed here.

Of course that could easily be fixed if either struct destructors or scope class returning were to be added to the language. Then you could just return something with a destructor that releases the lock.

Struct destructors will be added, but typesafe variadics are already in. I think Cout(a, b) is the most natural interface to offer. Andrei
Mar 29 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 That's great, however the interface has a problem too: it does not 
 produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output?

Cout. The thing is that the design using the syntax Cout(a)(b) is conducive to two separate calls to the library. I recall this has been briefly discussed here.

Of course that could easily be fixed if either struct destructors or scope class returning were to be added to the language. Then you could just return something with a destructor that releases the lock.

Struct destructors will be added, but typesafe variadics are already in. I think Cout(a, b) is the most natural interface to offer.

I dislike typesafe variadics for I/O, mainly because it'll instantiate a new version for every parameter list. I/O functions will typically be called with a lot of different parameter lists, which may lead to code bloat. However, my point in that post was that using the method I suggested (the first call returning a struct with a destructor) also has the advantage that it allows for backwards-compatibility. The current syntax will continue to work, with the only difference being that now it'll be thread-safe.
Mar 29 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 That's great, however the interface has a problem too: it does not 
 produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output?

Cout. The thing is that the design using the syntax Cout(a)(b) is conducive to two separate calls to the library. I recall this has been briefly discussed here.

Of course that could easily be fixed if either struct destructors or scope class returning were to be added to the language. Then you could just return something with a destructor that releases the lock.

Struct destructors will be added, but typesafe variadics are already in. I think Cout(a, b) is the most natural interface to offer.

I dislike typesafe variadics for I/O, mainly because it'll instantiate a new version for every parameter list. I/O functions will typically be called with a lot of different parameter lists, which may lead to code bloat.

I think this is not a problem (for a well-written library) as those many parameter lists will essentially be sliced and diced into the equivalent hand-written calls.
 However, my point in that post was that using the method I suggested 
 (the first call returning a struct with a destructor) also has the 
 advantage that it allows for backwards-compatibility. The current syntax 
 will continue to work, with the only difference being that now it'll be 
 thread-safe.

This is not going to work. Essentially you are giving the user the license to execute arbitrary code with locked files, a sure recipe for puzzling deadlocks. Cout(a, b) is the way. Andrei
Mar 29 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 I believe the current discussion is only about under-the-hood 
 implementation issues.  So I don't think you have to worry about any 
 D libraries exposing (good or bad) C/C++ design decisions to users.  
 Tango is going to expose the same D interface no matter how it's 
 implemented under the hood.

That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output?

Cout. The thing is that the design using the syntax Cout(a)(b) is conducive to two separate calls to the library. I recall this has been briefly discussed here.

Just making sure we were on the same page. In my experience, raw output is typically used for logging--that's about the only case I can think of where the random interleaving of messages is acceptable. Tango has a separate logging facility which performs synchronization for exactly this purpose: tango.util.log. Perhaps this particular critique would be more appropriately applied to the logging package? Sean
Mar 29 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 I believe the current discussion is only about under-the-hood 
 implementation issues.  So I don't think you have to worry about 
 any D libraries exposing (good or bad) C/C++ design decisions to 
 users.  Tango is going to expose the same D interface no matter how 
 it's implemented under the hood.

That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output?

Cout. The thing is that the design using the syntax Cout(a)(b) is conducive to two separate calls to the library. I recall this has been briefly discussed here.

Just making sure we were on the same page. In my experience, raw output is typically used for logging--that's about the only case I can think of where the random interleaving of messages is acceptable. Tango has a separate logging facility which performs synchronization for exactly this purpose: tango.util.log. Perhaps this particular critique would be more appropriately applied to the logging package?

I don't think so. For example, I have a multithreaded program that starts processes on multiple machines and outputs "stamped" lines to the standard output - lines with a prefix clarifying which machine the line comes from, and at what time. That output is gzipped for efficiency and transported via ethernet to a hub where the lines are demultiplexed and put into multiple files etc. It is essential that lines are written atomically, but beyond that, the actual order does not matter. Andrei
Mar 29 2007
parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 I believe the current discussion is only about under-the-hood 
 implementation issues.  So I don't think you have to worry about 
 any D libraries exposing (good or bad) C/C++ design decisions to 
 users.  Tango is going to expose the same D interface no matter 
 how it's implemented under the hood.

That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output?

Cout. The thing is that the design using the syntax Cout(a)(b) is conducive to two separate calls to the library. I recall this has been briefly discussed here.

Just making sure we were on the same page. In my experience, raw output is typically used for logging--that's about the only case I can think of where the random interleaving of messages is acceptable. Tango has a separate logging facility which performs synchronization for exactly this purpose: tango.util.log. Perhaps this particular critique would be more appropriately applied to the logging package?

I don't think so. For example, I have a multithreaded program that starts processes on multiple machines and outputs "stamped" lines to the standard output - lines with a prefix clarifying which machine the line comes from, and at what time. That output is gzipped for efficiency and transported via ethernet to a hub where the lines are demultiplexed and put into multiple files etc. It is essential that lines are written atomically, but beyond that, the actual order does not matter.

This sounds to me like logging output, which is exactly what I described. The logger could be attached to the console or a socket as easily as a file. Why use Cout for this? Sean
Mar 29 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 I believe the current discussion is only about under-the-hood 
 implementation issues.  So I don't think you have to worry about 
 any D libraries exposing (good or bad) C/C++ design decisions to 
 users.  Tango is going to expose the same D interface no matter 
 how it's implemented under the hood.

That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs.

Which interface? And are you talking about input or output?

Cout. The thing is that the design using the syntax Cout(a)(b) is conducive to two separate calls to the library. I recall this has been briefly discussed here.

Just making sure we were on the same page. In my experience, raw output is typically used for logging--that's about the only case I can think of where the random interleaving of messages is acceptable. Tango has a separate logging facility which performs synchronization for exactly this purpose: tango.util.log. Perhaps this particular critique would be more appropriately applied to the logging package?

I don't think so. For example, I have a multithreaded program that starts processes on multiple machines and outputs "stamped" lines to the standard output - lines with a prefix clarifying which machine the line comes from, and at what time. That output is gzipped for efficiency and transported via ethernet to a hub where the lines are demultiplexed and put into multiple files etc. It is essential that lines are written atomically, but beyond that, the actual order does not matter.

This sounds to me like logging output, which is exactly what I described.

Oh, I thought you meant logging as just auxiliary informative message as opposed to the meat of the I/O. Just to clarify: the interleaved output is the meat of the I/O in the case above.
 The logger could be attached to the console or a socket as 
 easily as a file. Why use Cout for this?

"Because it's there!" :o) All a bona fide programmer expects is to have access to the three pre-opened standard streams and just use them. I'm not sure how to make a logger output to stdout. The manual (after an introduction that makes it pretty clear I'm already swimming upstream by using logger for something else than logging) says: // send output of myLogger to stderr myLogger.addAppender(new ConsoleAppender()); Later in the document there's the section "A Custom Appender Example" which implements what I need - not in the library, in userland. Caveat: I don't know whether that example keeps things properly multithreaded. If it does, it's deadlock-prone as it allows arbitrary code to be executed with locked files. If it doesn't, it's incorrect. Doomed either way. The lure of just using phobos' and C's stdio is so much stronger :o). 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). (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. (c) And not for programs linking with anything that uses C's stdio. For those, use Phobos. Andrei
Mar 29 2007
parent reply Sean Kelly <sean f4.ca> writes:
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. Sean
Mar 29 2007
next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
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
Mar 29 2007
prev sibling parent reply kris <foo bar.com> writes:
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.
Mar 29 2007
parent Sean Kelly <sean f4.ca> writes:
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
Mar 29 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:

 Andrei Alexandrescu (See Website For Email) wrote:

 kris wrote:

 Last week there were a series of posts regarding some optimized 
 code within phobos streams. A question posed was, without those 
 same optimizations, would tango.io be slower than the improved 
 phobos [1]

 As these new phobos IO functions are now available, Andrei's 
 "benchmark" [2] was run on both Win32 and linux to see where 
 tango.io could use some improvement.

[snip] On my machine, Tango does 4.3 seconds and the following phobos program (with Walter's readln) does 5.4 seconds:

On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.

If I understand things correctly, it looks like the hope is to derive more speed from further dropping phobos and C I/O compatibility, a path that I personally don't consider attractive.

Nope. That's not the case at all. The expectation (or 'hope', if you like) is that we can make the linux version operate more like the Win32 version
 Also, the fact that the tango version is "more than twice as efficient 
 as the fastest C version identified" suggests a problem with the 
 testing method or with the C code. Are they comparable? If you 
 genuinely have a method to push bits through two times faster than the 
 fastest C can do, you may want as well go ahead and patent it. Your 
 method would speed up many programs, since many use C's I/O and are 
 I/O bound. It's huge news. 

That's good for D then? There's no reason why C could not take the same approach yet, one might imagine, the IO strategies exposed and the wide variety of special cases may 'discourage' the implementation of a more efficient approach? That's just pure speculation on my part, and I'm quite positive the C version could be sped up notably if one reimplemented a bunch of things.
 I'm not even kidding. But I doubt that that's the case.

You're most welcome to your doubts, Andrei. However, just because "C does it that way" doesn't mean it is, or ever was, the "best" approach

I think we're not on the same page here. What I'm saying is that, unless you cut a deal with Microsoft to provide you with a secret D I/O API that nobody knows about, all fast APIs in existence come with a C interface. It's very hard to contend that. Probably you are referring to the C stdio, and I'm in agreement with that. Of course there's a variety of means to be faster than stdio on any given platform, at various compatibility costs. It's known how to do that. "Hot water has been invented."
 Also, the Tango version has a bug. Running Tango's cat without any 
 pipes does not read lines from the console and outputs them one by 
 one, as it should; instead, it reads many lines and buffers them 
 internally, echoing them only after the user has pressed end-of-file 
 (^D on Linux), or possibly after the user has entered a large amount 
 of data (I didn't have the patience). The system cat program and the 
 phobos implementation correctly process each line as it was entered.

If you mean something that you've written, that could presumeably be rectified by adding the isatty() test Walter had mentioned before. That has not been added to tango.io since (a) it would likely make programs behave differently depending on whether they were redirected or not. It's not yet clear whether that is an appropriate specialization, as default behaviour

What is absolutely clear is that the current version has a bug. It can't read a line from the user and write it back. There cannot be any question that that's a problem.

Only with the way that you've written your program. In the general case, that is not true at all. But please do submit that bug-report :)

This is the fourth time we need to discuss this. Why do I need to _argue_ that this is a bug, I don't understand. Let me spell it again: Cin.nextLine is incorrect. It cannot be used (without possibly some extra incantations I don't know about) to implement a program that does this: $ ./test.d Please enter your name: Moe Hello, Moe! $ _ I don't have an account on the Tango site, and in a fraction of the time it would take me to create one, you can submit the bug report.
 , and (b) there has been no ticket issued for it

 Again, please submit a ticket so we don't forget about that detail. 
 We'd be interested to hear if folk think the "isatty() test" should 
 be default behaviour, or would perhaps lead to corner-case issues 
 instead

I was actually pointing out a larger issue: incompatibility with phobos' I/O and C I/O. Tango's version is now faster (thank God we got past the \n issue and bummer it's not the default parameter of nextLine) but it is incompatible with both phobos' and C's stdio. (It's possible that the extra speed is derived from skipping C's stdio and using read and write directly.) Probably you could reimplement phobos and bundle it with Tango to give the users the option to link phobos code with Tango code properly, but still C stdio compatibility is lost, and phobos code has access to it.

The issue you raise here is that of interleaved and shared access to global entities, such as the console, where some incompatability between tango.io and C IO is exhibited. If you really dig into it, you'll perhaps conclude that (a) the number of real-world scenario where this would truly become an issue is diminishingly small, and (b) the vast (certainly on Win32) performance improvement is worth that tradeoff. Even then, it is certainly possible to intercept C IO functions and route them to tango.io equivalents instead.

What Win32 primitives does tango use?
 It has been said before, but is probably worth repeating:
 
 - Tango is not a phobos clone. Nor is it explicitly designed to be 
 compatible with phobos; sometimes it is worthwhile taking a different 
 approach. Turns out that phobos can be run alongside tango in many 
 situations.
 
 - Tango is for D programmers; not C programmers.
 
 - Tango, as a rule, is intended to be flexible, modular, efficient and 
 practical. The goal is to provide D with an exceptional library, and we 
 reserve the right to break a few eggs along the way ;)

Sounds great. Andrei
Mar 28 2007
next sibling parent kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 I don't have an account on the Tango site, and in a fraction of the time 
 it would take me to create one, you can submit the bug report.

We ask ppl to create tickets so that we can track and schedule the issue, have a potential ongoing dialogue with those involved, and so that we have something to detail the changes for each release. If you'd prefer to be kept out of the loop on this, that's a perfectly valid option
Mar 29 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:

 Andrei Alexandrescu (See Website For Email) wrote:

 kris wrote:

 Last week there were a series of posts regarding some optimized 
 code within phobos streams. A question posed was, without those 
 same optimizations, would tango.io be slower than the improved 
 phobos [1]

 As these new phobos IO functions are now available, Andrei's 
 "benchmark" [2] was run on both Win32 and linux to see where 
 tango.io could use some improvement.

[snip] On my machine, Tango does 4.3 seconds and the following phobos program (with Walter's readln) does 5.4 seconds:

On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.

If I understand things correctly, it looks like the hope is to derive more speed from further dropping phobos and C I/O compatibility, a path that I personally don't consider attractive.

Nope. That's not the case at all. The expectation (or 'hope', if you like) is that we can make the linux version operate more like the Win32 version
 Also, the fact that the tango version is "more than twice as 
 efficient as the fastest C version identified" suggests a problem 
 with the testing method or with the C code. Are they comparable? If 
 you genuinely have a method to push bits through two times faster 
 than the fastest C can do, you may want as well go ahead and patent 
 it. Your method would speed up many programs, since many use C's I/O 
 and are I/O bound. It's huge news. 

That's good for D then? There's no reason why C could not take the same approach yet, one might imagine, the IO strategies exposed and the wide variety of special cases may 'discourage' the implementation of a more efficient approach? That's just pure speculation on my part, and I'm quite positive the C version could be sped up notably if one reimplemented a bunch of things.
 I'm not even kidding. But I doubt that that's the case.

You're most welcome to your doubts, Andrei. However, just because "C does it that way" doesn't mean it is, or ever was, the "best" approach

I think we're not on the same page here. What I'm saying is that, unless you cut a deal with Microsoft to provide you with a secret D I/O API that nobody knows about, all fast APIs in existence come with a C interface. It's very hard to contend that. Probably you are referring to the C stdio, and I'm in agreement with that. Of course there's a variety of means to be faster than stdio on any given platform, at various compatibility costs. It's known how to do that. "Hot water has been invented."
 Also, the Tango version has a bug. Running Tango's cat without any 
 pipes does not read lines from the console and outputs them one by 
 one, as it should; instead, it reads many lines and buffers them 
 internally, echoing them only after the user has pressed 
 end-of-file (^D on Linux), or possibly after the user has entered a 
 large amount of data (I didn't have the patience). The system cat 
 program and the phobos implementation correctly process each line 
 as it was entered.

If you mean something that you've written, that could presumeably be rectified by adding the isatty() test Walter had mentioned before. That has not been added to tango.io since (a) it would likely make programs behave differently depending on whether they were redirected or not. It's not yet clear whether that is an appropriate specialization, as default behaviour

What is absolutely clear is that the current version has a bug. It can't read a line from the user and write it back. There cannot be any question that that's a problem.

Only with the way that you've written your program. In the general case, that is not true at all. But please do submit that bug-report :)

This is the fourth time we need to discuss this. Why do I need to _argue_ that this is a bug, I don't understand. Let me spell it again: Cin.nextLine is incorrect. It cannot be used (without possibly some extra incantations I don't know about) to implement a program that does this: $ ./test.d Please enter your name: Moe Hello, Moe! $ _

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 )( "!" ); }
 I don't have an account on the Tango site, and in a fraction of the time 
 it would take me to create one, you can submit the bug report.

True. But it's more effective for the person who understands the problem to submit the bug report.
 What Win32 primitives does tango use?

ReadFile and WriteFile. Sean
Mar 29 2007
parent reply kris <foo bar.com> writes:
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); }
Mar 29 2007
next sibling parent reply Roberto Mariottini <rmariottini mail.com> writes:
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); }

I think I'll take a look at Tango. Is there a tutorial, or better an examples library? Ciao
Mar 29 2007
parent Lars Ivar Igesund <larsivar igesund.net> writes:
Roberto Mariottini 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); }

I think I'll take a look at Tango. Is there a tutorial, or better an examples library? Ciao

The website over at http://www.dsource.org/projects/tango has various documentation (and also a few tutorials). You will also find quite a few small examples in the downloads. -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Mar 29 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
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. It's funny this does not happen exactly because of buffering, but the program has no control over the buffering so it should assume flushing could happen at any time. So the correct code is: auto name = Cin.get; Cout("Hello, ")(name); Andrei
Mar 29 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
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. Perhaps an aggressive inlining mechanism could violate this? Sean
Mar 29 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 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. Perhaps an aggressive inlining mechanism could violate this?

Actually if you go a bit lower-level (explicit 'this'), that line is equivalent to something like: --- write(write(Cout, "Hello, "), get(Cin)); --- or --- write(get(Cin), write("Hello, ", Cout)); --- depending on whether the calling convention passes 'this' first or last. (except with more complicated function names because of mangling, obviously) So actually 'this' is just another parameter. As such, evaluation can happen before or after other parameters. If the parameters happen to be evaluated in the "wrong" order (left-to-right in the first case, right-to-left in the second) the Cout("Hello, ") gets evaluated before write("Hello, ", Cout). If it happens to be in the "right" order, it's the other way around. But I'm pretty sure there's no guarantee. For that matter, 'this' could be treated specially (e.g. always passed in a specific register not otherwise used in the calling convention) and then there's no way to tell what the evaluation order will be, except for a specific implementation. What I'm trying to say here is basically this: you shouldn't rely on the order of evaluation of parameters. Not even if one of them happens to be used as 'this' for the method in question.
Mar 29 2007
next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 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. Perhaps an aggressive inlining mechanism could violate this?

Actually if you go a bit lower-level (explicit 'this'), that line is equivalent to something like: --- write(write(Cout, "Hello, "), get(Cin)); --- or --- write(get(Cin), write("Hello, ", Cout)); --- depending on whether the calling convention passes 'this' first or last. (except with more complicated function names because of mangling, obviously) So actually 'this' is just another parameter. As such, evaluation can happen before or after other parameters. If the parameters happen to be evaluated in the "wrong" order (left-to-right in the first case, right-to-left in the second) the Cout("Hello, ") gets evaluated before write("Hello, ", Cout). If it happens to be in the "right" order, it's the other way around. But I'm pretty sure there's no guarantee. For that matter, 'this' could be treated specially (e.g. always passed in a specific register not otherwise used in the calling convention) and then there's no way to tell what the evaluation order will be, except for a specific implementation. What I'm trying to say here is basically this: you shouldn't rely on the order of evaluation of parameters. Not even if one of them happens to be used as 'this' for the method in question.

Exactly so, thanks for the clear explanation. Andrei
Mar 29 2007
prev sibling parent reply kris <foo bar.com> writes:
Frits van Bommel wrote:
[snip]
 What I'm trying to say here is basically this: you shouldn't rely on the 
 order of evaluation of parameters. Not even if one of them happens to be 
 used as 'this' for the method in question.

Absolutely agree. Yet, without wishing to go off on a tangent, we're talking about call-chaining and not parameter evaluation-order. These are different things, and there was a painfully long thread on exactly this subject a year or two ago. Walter closed that one by stating call-chaining can only realistically evaluate from left to right (or something to that effect) and that's what D does. I'm sure JCC could locate that thread in a heartbeat ... don't know how he does that, but he sure is effective at it :)
Mar 29 2007
parent reply jcc7 <technocrat7 gmail.com> writes:
== Quote from kris (foo bar.com)'s article
 Frits van Bommel wrote:
 [snip]
 What I'm trying to say here is basically this: you shouldn't rely on the
 order of evaluation of parameters. Not even if one of them happens to be
 used as 'this' for the method in question.

talking about call-chaining and not parameter evaluation-order. These are different things, and there was a painfully long thread on exactly this subject a year or two ago. Walter closed that one by stating call-chaining can only realistically evaluate from left to right (or something to that effect) and that's what D does. I'm sure JCC could locate that thread in a heartbeat ... don't know > how he

These days, I Usually just use Google and restrict it to www.digitalmars.com (such as the search box on the Digital Mars webpage does). I can't seem to track down this one though. My technique works best when I actually remember the particular post or discussion (and it works really well when I remember that I myself posted in the discussion). And I don't really remember this discussion. Do you happen to remember who Walter was replying to? That could be helpful. I can't seem to find the right keywords to find the particular topic. I tried various combinations of "newshound" (Walter's e-mail), "evaluation", "order", "operator", "operation", "call", "chain", "return", "function", etc. Also, sometimes it's helpful if I know when the discussion took place (Last month? Last year? 2005?). But this might just be a needle that remains hidden in the haystack. Perhaps we can just talk Walter into putting this principle in the D Specification. ;) jcc7
Mar 29 2007
parent reply kris <foo bar.com> writes:
jcc7 wrote:
 == Quote from kris (foo bar.com)'s article
 
Frits van Bommel wrote:
[snip]

What I'm trying to say here is basically this: you shouldn't rely on the
order of evaluation of parameters. Not even if one of them happens to be
used as 'this' for the method in question.

Absolutely agree. Yet, without wishing to go off on a tangent, we're talking about call-chaining and not parameter evaluation-order. These are different things, and there was a painfully long thread on exactly this subject a year or two ago. Walter closed that one by stating call-chaining can only realistically evaluate from left to right (or something to that effect) and that's what D does. I'm sure JCC could locate that thread in a heartbeat ... don't know > how he

does that, but he sure is effective at it :) These days, I Usually just use Google and restrict it to www.digitalmars.com (such as the search box on the Digital Mars webpage does). I can't seem to track down this one though. My technique works best when I actually remember the particular post or discussion (and it works really well when I remember that I myself posted in the discussion). And I don't really remember this discussion. Do you happen to remember who Walter was replying to? That could be helpful. I can't seem to find the right keywords to find the particular topic. I tried various combinations of "newshound" (Walter's e-mail), "evaluation", "order", "operator", "operation", "call", "chain", "return", "function", etc. Also, sometimes it's helpful if I know when the discussion took place (Last month? Last year? 2005?). But this might just be a needle that remains hidden in the haystack. Perhaps we can just talk Walter into putting this principle in the D Specification. ;) jcc7

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?)
Mar 29 2007
parent reply jcc7 <technocrat7 gmail.com> writes:
== Quote from kris (foo bar.com)'s article
 jcc7 wrote:
 == Quote from kris (foo bar.com)'s article

Frits van Bommel wrote:
[snip]

What I'm trying to say here is basically this: you shouldn't rely on the
order of evaluation of parameters. Not even if one of them happens to be
used as 'this' for the method in question.

Absolutely agree. Yet, without wishing to go off on a tangent, we're talking about call-chaining and not parameter evaluation-order. These are different things, and there was a painfully long thread on exactly this subject a year or two ago. Walter closed that one by stating call-chaining can only realistically evaluate from left to right (or something to that effect) and that's what D does. I'm sure JCC could locate that thread in a heartbeat ... don't know > how he

does that, but he sure is effective at it :) These days, I Usually just use Google and restrict it to www.digitalmars.com (such as the search box on the Digital Mars webpage does). I can't seem to track down this one though. My technique works best when I actually remember the particular post or discussion (and it works really well when I remember that I myself posted in the discussion). And I don't really remember this discussion. Do you happen to remember who Walter was replying to? That could be helpful. I can't seem to find the right keywords to find the particular topic. I tried various combinations of "newshound" (Walter's e-mail), "evaluation", "order", "operator", "operation", "call", "chain", "return", "function", etc. Also, sometimes it's helpful if I know when the discussion took place (Last month? Last year? 2005?). But this might just be a needle that remains hidden in the haystack. Perhaps we can just talk Walter into putting this principle in the D


 jcc7

of call-chaining in Mango, and resulted in someone leaving the NG for good (was his name manfred or something?)

I think this is what you're looking for: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=31264 or http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=31264 It's part of a much larger discussion: http://www.digitalmars.com/d/archives/digitalmars/D/31069.html#N31264 (I couldn't find where anyone vowed to leave the newsgroup forever, but I suppose that does happen from time to time.) jcc7
Mar 29 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
jcc7 wrote:
 == Quote from kris (foo bar.com)'s article
 jcc7 wrote:
 == Quote from kris (foo bar.com)'s article

 Frits van Bommel wrote:
 [snip]

 What I'm trying to say here is basically this: you shouldn't rely on the
 order of evaluation of parameters. Not even if one of them happens to be
 used as 'this' for the method in question.

talking about call-chaining and not parameter evaluation-order. These are different things, and there was a painfully long thread on exactly this subject a year or two ago. Walter closed that one by stating call-chaining can only realistically evaluate from left to right (or something to that effect) and that's what D does. I'm sure JCC could locate that thread in a heartbeat ... don't know > how he

These days, I Usually just use Google and restrict it to www.digitalmars.com (such as the search box on the Digital Mars webpage does). I can't seem to track down this one though. My technique works best when I actually remember the particular post or discussion (and it works really well when I remember that I myself posted in the discussion). And I don't really remember this discussion. Do you happen to remember who Walter was replying to? That could be helpful. I can't seem to find the right keywords to find the particular topic. I tried various combinations of "newshound" (Walter's e-mail), "evaluation", "order", "operator", "operation", "call", "chain", "return", "function", etc. Also, sometimes it's helpful if I know when the discussion took place (Last month? Last year? 2005?). But this might just be a needle that remains hidden in the haystack. Perhaps we can just talk Walter into putting this principle in the D


 jcc7

of call-chaining in Mango, and resulted in someone leaving the NG for good (was his name manfred or something?)

I think this is what you're looking for: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=31264 or http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=31264

The information is not there.
 It's part of a much larger discussion:
 http://www.digitalmars.com/d/archives/digitalmars/D/31069.html#N31264

After a summary read I saw this: classRef.method1().method2().method3(); In this case, indeed the three methods are evaluated in sequence. It would be, however, a mistake to infer from that that the code: Cout("Hello, ")(Cin.get); guaranteedly reads the console before having printed "Hello, ". It doesn't. Andrei
Mar 29 2007
next sibling parent jcc7 <technocrat7 gmail.com> writes:
== Quote from Andrei Alexandrescu (See Website For Email)
(SeeWebsiteForEmail erdani.org)'s article
 jcc7 wrote:
 == Quote from kris (foo bar.com)'s article
 jcc7 wrote:
 == Quote from kris (foo bar.com)'s article

 Frits van Bommel wrote:
 [snip]

 What I'm trying to say here is basically this: you shouldn't rely on the
 order of evaluation of parameters. Not even if one of them happens to be
 used as 'this' for the method in question.

talking about call-chaining and not parameter evaluation-order. These are different things, and there was a painfully long thread on exactly this subject a year or two ago. Walter closed that one by stating call-chaining can only realistically evaluate from left to right (or something to that effect) and that's what D does. I'm sure JCC could locate that thread in a heartbeat ... don't know > how he

These days, I Usually just use Google and restrict it to www.digitalmars.com




 as the search box on the Digital Mars webpage does).

 I can't seem to track down this one though. My technique works best when I
 actually remember the particular post or discussion (and it works really




 I remember that I myself posted in the discussion). And I don't really remember
 this discussion.

 Do you happen to remember who Walter was replying to? That could be helpful. I
 can't seem to find the right keywords to find the particular topic. I tried
 various combinations of "newshound" (Walter's e-mail), "evaluation", "order",
 "operator", "operation", "call", "chain", "return", "function", etc. Also,
 sometimes it's helpful if I know when the discussion took place (Last month?




 year? 2005?). But this might just be a needle that remains hidden in the




 Perhaps we can just talk Walter into putting this principle in the D


 jcc7

of call-chaining in Mango, and resulted in someone leaving the NG for good (was his name manfred or something?)

I think this is what you're looking for:


 or


 The information is not there.
 It's part of a much larger discussion:
 http://www.digitalmars.com/d/archives/digitalmars/D/31069.html#N31264

classRef.method1().method2().method3(); In this case, indeed the three methods are evaluated in sequence. It would be, however, a mistake to infer from that that the code: Cout("Hello, ")(Cin.get); guaranteedly reads the console before having printed "Hello, ". It doesn't. Andrei

Well, I don't entired understand the issue, but I may have found the wrong posting. Or maybe there is no posting where Walter addresses this issue. jcc7
Mar 29 2007
prev sibling parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 After a summary read I saw this:
 
 classRef.method1().method2().method3();
 
 In this case, indeed the three methods are evaluated in sequence. It 
 would be, however, a mistake to infer from that that the code:
 
 Cout("Hello, ")(Cin.get);
 
 guaranteedly reads the console before having printed "Hello, ". It doesn't.

Nobody claimed that it "guaranteedly" (whatever that means) does in the general case, so let's please move on? The only thing asserted is that call-chaining is evaluated in left-to-right order. I think we can safely put that back to bed again
Mar 29 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 After a summary read I saw this:

 classRef.method1().method2().method3();

 In this case, indeed the three methods are evaluated in sequence. It 
 would be, however, a mistake to infer from that that the code:

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

 guaranteedly reads the console before having printed "Hello, ". It 
 doesn't.

Nobody claimed that it "guaranteedly" (whatever that means) does in the general case, so let's please move on? The only thing asserted is that call-chaining is evaluated in left-to-right order. I think we can safely put that back to bed again

You wrote this: ----------------------- 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); } ----------------------- The code is incorrect. Point blank. Right? Andrei
Mar 29 2007
parent kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:

 Nobody claimed that it "guaranteedly" (whatever that means) does in 
 the general case, so let's please move on?

 The only thing asserted is that call-chaining is evaluated in 
 left-to-right order. I think we can safely put that back to bed again

You wrote this: ----------------------- 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); } ----------------------- The code is incorrect. Point blank. Right?

It operates just fine due to the underlying mechanisms, so "point blank" is making a big drama out of it. It does not illustrate good practice, however. If you wish to write a book about that, be my guest :-D
Mar 29 2007
prev sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
jcc7 wrote:
 == Quote from kris (foo bar.com)'s article
 jcc7 wrote:
 == Quote from kris (foo bar.com)'s article

 Frits van Bommel wrote:
 [snip]

 What I'm trying to say here is basically this: you shouldn't rely on the
 order of evaluation of parameters. Not even if one of them happens to be
 used as 'this' for the method in question.

talking about call-chaining and not parameter evaluation-order. These




Actually, we *were* talking about parameter evaluation-order in this subthread. From the post I was replying to: --- 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 --- (emphasis added)
 are different things, and there was a painfully long thread on exactly
 this subject a year or two ago.
 Walter closed that one by stating call-chaining can only realistically
 evaluate from left to right (or something to that effect) and that's
 what D does.




Yes, call-chaining can only evaluate left-to-right, but the parameters *passed* to the calls can be evaluated in any order.
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=31264

(Assuming that's the correct thread) The closest thing to "Walter closed that one by stating call-chaining can only realistically evaluate from left to right" I can find is this post[1]: --- Walter Bright Wrote:
 "Manfred Nowak" <svv1999 hotmail.com> wrote in message
 news:Xns9727D45B325B1svv1999hotmailcom 63.105.9.61...
 Moreover the behaviour of the whisper notation is undefined according
 to the specs, which clearly state that the order of all expression
 evaluations are undefined if not explicitely defined---and no
 definition for the whisper notation is given.

I've been intending for a while now to revise that to make the order of evaluation explicit. The undefined order of evaluation does not offer

 benefits, and causes a lot of headaches.

 But there are two different things here: order of evaluation, and 

 precedence. D operator precedence is clearly defined. For example:

     a + b + c

 is defined to be:

     (a + b) + c

 There is no ambiguity. The order of evaluation ambiguity comes from the
 order a, b, and c are evaluated. For example, for:

 void a() { writefln("a"); }
 void b() { writefln("b"); }
 void c() { writefln("c"); }
 ...
 a() + b() + c()

 may output one of:

 abc
 acb
 bac
 bca
 cab
 cba

All he said was that the translation from expression to explicit opAdd calls is fixed by operator precedence, and that he was thinking about defining order of evaluation for the parameters in an expression. However, nowhere did I see *how* he intended to define it, nor that he ever did so. [1]: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=31263
Mar 29 2007
parent reply kris <foo bar.com> writes:
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?
 All he said was that the translation from expression to explicit opAdd 
 calls is fixed by operator precedence, and that he was thinking about 
 defining order of evaluation for the parameters in an expression. 
 However, nowhere did I see *how* he intended to define it, nor that he 
 ever did so.

Then I suggest you ask Walter? Please do so on a different thread, since this one has too many topics already? :)
Mar 29 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
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. Cout("Hello, ", Cin.get) would remain correct in any instance if it existed. Andrei
Mar 29 2007
parent reply kris <foo bar.com> writes:
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). What I had referred to vis-a-vis the prior thread was simply that call-chaining had been resolved in terms of eval-order. It evaluated left to right. I'd try to help you understand all of the 'questions' you've recently brought up, but you don't appear to be the slightest bit interested in trying to understand much; sadly. I'm open to real discussion if that's what you'd prefer, yet your demeanour has been and continues to be that of someone with a hidden adgenda or with a axe to grind. If you'll drop that attitute forthwith, I'll be happy to continue
Mar 29 2007
next sibling parent reply James Dennett <jdennett acm.org> writes:
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
Mar 29 2007
next sibling parent reply kris <foo bar.com> writes:
James Dennett wrote:
 kris wrote:

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
Mar 29 2007
parent reply James Dennett <jdennett acm.org> writes:
kris wrote:
 James Dennett wrote:
 kris wrote:

 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.
Mar 29 2007
parent reply kris <foo bar.com> writes:
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.
Mar 29 2007
parent reply James Dennett <jdennett acm.org> writes:
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
Mar 29 2007
parent reply kris <foo bar.com> writes:
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?
Mar 29 2007
parent reply James Dennett <jdennett acm.org> writes:
kris wrote:
 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?

Yes, that's what I was trying to do.
 First, there is the
 question of call chaining operating in an ordered fashion.

If you mean the same thing that I do by "call chaining", I've not had any doubt about it; simple logic forces it.
 Second,
 there's the question of whether the miserable example is "correct" or
 not. Would you agree?

There are multiple questions about the correctness of the example. One of them relates to whether Cin.get can be called before output is written; others may be affected by buffering.
 Did you read the message of Walter's to which I referred?

Naturally

Good; I thought I'd check.
 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 point I quoted for was the "otherwise the order is undefined", which doesn't apply to only the opCall calls.
 The only question remaining is that of what happens with three chained
 calls.

No, it is not. The issue you've not addressed is that the right-hand argument to the second opCall might be evaluated before its left-hand argument. That's not about "call chaining" according to any definition that I have seen. It's about function argument evaluation order. Would you please be able to address this point? It's the crucial one, and (to my mind) the only one where there appears to be substantive disagreement.
 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.

I've not seen a coherent explanation of why it cannot break if evaluation order is changed, except...
 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.

This covers it, thank you. The output function may or may not be called before Cin.get, but it doesn't matter as the actual output will be buffered in this case, given the small amount of it.
 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 ;)

What is that? (Seriously.)
 I do hope that helps?

It does, thank you. We have just the one issue now, and it's not interesting in the case of the little example program. -- James
Mar 30 2007
parent kris <foo bar.com> writes:
James Dennett wrote:
 kris wrote:

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

What is that? (Seriously.)

The example was known to be a tricky one, and the reaction to it was "strong"; it was being leveraged to hoist straw-men and at one point became "troll fodder" (for want of a better term). Then there was a vague concern that Walter might be dropping support for call-chaining; thankfully that turned out to be incorrect. My bewiliderment over that led the word "mystery" within the subject
I do hope that helps?

It does, thank you.

you are welcome
Mar 30 2007
prev sibling next sibling parent reply James Dennett <jdennett acm.org> writes:
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.

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

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
Mar 29 2007
next sibling parent Roberto Mariottini <rmariottini mail.com> writes:
James Dennett wrote:
[...]
 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 agree that this is "flaming". But I also think that Andrei's behavior is "trolling".
 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.

I also think that Kris is a valued contributor. I just don't understand why Andrei is against Tango. He surely has its motivations, but I don't see them. Ciao
Mar 30 2007
prev sibling parent kris <foo bar.com> writes:
James Dennett wrote:
[snip]
 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.

That's cool, James. You'll also please just accept that I'll sometimes call things as I see them? I've seen Andrei make "attacks" upon others, I've witnessed the manner in which he operates, and I've been the recipient of his 'distaste' on several occasions. It's not in my interest to take issue with anyone in particular, yet I don't have much patience for stupid games either, particularly when there's the smell of an agenda in the air. I called Andrei out, and you don't like the way I approached it. I can, and will, happily accept that. And, I do appreciate you stepping up and speaking your mind too. If you'd like further clarification, I'd be glad to discuss it offline.
 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.

Yes, I fully agree
Mar 30 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
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.

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

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. After following Andrei's posts, I tend to agree with Kris' perspective. Andrei does come across as having an agenda. I can accept that given his position in the influence of D; but it's becoming very hard to separate truth from perspective in all these posts. Why is it wrong to state that Andrei has an agenda? He does, doesn't he? Are we all afraid to admit it? I just don't think he's going about it very honestly. If he were trully seeking to be helpful to Tango design ideas, he would be contributing over in the Tango forums... Since he doesn't do that, it's quite easy to see why one might think he has an agenda... and not in Tango's favour. Andrei does tend to push his preferences on the community. That deserves to be balanced. Kris, Sean, and others have been doing their best to be clear and fair in regards to addressing Tango "propaganda"; if Andrei makes allegations about Tango, they are obligated to either defend design decisions or accept recommendations as feasible and beneficial. I think they've been doing a very good job of both given the circumstances. Are people not noticing this? I don't recall ever seeing Andrei admit he is wrong without some equivication or deflection. If he does, it's often hidden in some sorty joke or distraction. He's just not a straight forward person, plain and simple. Either that or he has trouble with humility. He's got oodles of creativity, good ideas, experience... and personal opinions (like many here) and maybe even a little bit of academic rigour to back him up :). He makes tons of :O) faces. But these matter little, if he serves his own preferences, his own interests, and his own agenda. Maybe I got him wrong, but from what I've seen, I doubt it.

The only civilized response I can imagine to this is killfiling sender's address. It's understandable for anyone to get heated and make a few ad hominem arguments in the midst of a heated argument. But to actually sit down and build an entire web of assumptions leading to sweeping personality judgments, that's just shady. Put yourself for a minute at the receiving end of such calumny and you'll understand why you ought to be ashamed of yourself. Andrei
Mar 30 2007
parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 John Reimer wrote:

 Andrei does come across as having an agenda.  I can accept that given his
 position in the influence of D; but it's becoming very hard to separate
 truth from perspective in all these posts.  Why is it wrong to state that
 Andrei has an agenda?  He does, doesn't he?  Are we all afraid to 
 admit it?
 I just don't think he's going about it very honestly. If he were trully
 seeking to be helpful to Tango design ideas, he would be contributing 
 over
 in the Tango forums... Since he doesn't do that, it's quite easy to see
 why one might think he has an agenda... and not in Tango's favour.

 Andrei does tend to push his preferences on the community. That
 deserves to be balanced.  Kris, Sean, and others have been doing their 
 best
 to be clear and fair in regards to addressing Tango "propaganda"; if
 Andrei makes allegations about Tango, they are obligated to
 either defend design decisions or accept recommendations as feasible and
 beneficial.  I think they've been doing a very good job of both given the
 circumstances.  Are people not noticing this?
 I don't recall ever seeing Andrei admit he is wrong without some
 equivication or deflection. If he does, it's often hidden in some sorty
 joke or distraction. He's just not a straight forward person, plain and
 simple.  Either that or he has trouble with humility.

 He's got oodles of creativity, good ideas, experience... and personal
 opinions (like many here) and maybe even a little bit of academic 
 rigour to
 back him up :). He makes tons of :O) faces. But these matter little, 
 if he
 serves his own preferences, his own interests, and his own agenda.  Maybe
 I got him wrong, but from what I've seen, I doubt it.

The only civilized response I can imagine to this is killfiling sender's address. It's understandable for anyone to get heated and make a few ad hominem arguments in the midst of a heated argument. But to actually sit down and build an entire web of assumptions leading to sweeping personality judgments, that's just shady.

This "shady" claim is surely an outrageous manipulation coming from someone who has recently exhibited the attributes of a troll? JJR is one of the finest and fairest people who hang around here; he's known to be a humble and caring individual, with a certain distaste for confrontation. For him to write what he did indicates a level of discontent beyond the pale, and it seems evident that this "web of assumptions" you lay at his feet is something instigated and shaped by your own hand.
 Put yourself for a minute at 
 the receiving end of such calumny and you'll understand why you ought to 
 be ashamed of yourself.

Then please consider your own behaviour, and the "receiving end" of those upon whom you have poured scorn? From the time you had a go at Tom through to this point, it is my opinion that you have at various times exhibited the attributes of a bigot, a bully, a megalomaniac and a troll. There will be others who share this view, and those who don't: the only thing that matters right here, given the history, is your audacity to "play to the gallery" as some /injured/ party. If there's anyone who should be ashamed, it is yourself Andrei. You've been wholly effective at frittering away the wealth of respect you had commanded (from me at least), through the kind of behaviour that John described and Roberto *nailed* with his comment about your "trolling". I'm no angel, yet I'll willingly shoulder responsibility for my actions and retract or apologize sincerely where I've made a mistake. On the other hand, I've yet to see a case (as John noted) where you've shown any remorse or real corrective action for anything questionable you've been party to. In truth, I've seen you ardently follow a course in the opposite direction. You are welcome to do that, of course, but don't start playing the injured & innocent and expect to walk away in a ray of sunshine. [yes, I'll likely be hauled over the coals on this post too; yet sometimes it is better to first get the moose onto the table, and go from there]
Mar 30 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 John Reimer wrote:

 Andrei does come across as having an agenda.  I can accept that given 
 his
 position in the influence of D; but it's becoming very hard to separate
 truth from perspective in all these posts.  Why is it wrong to state 
 that
 Andrei has an agenda?  He does, doesn't he?  Are we all afraid to 
 admit it?
 I just don't think he's going about it very honestly. If he were trully
 seeking to be helpful to Tango design ideas, he would be contributing 
 over
 in the Tango forums... Since he doesn't do that, it's quite easy to see
 why one might think he has an agenda... and not in Tango's favour.

 Andrei does tend to push his preferences on the community. That
 deserves to be balanced.  Kris, Sean, and others have been doing 
 their best
 to be clear and fair in regards to addressing Tango "propaganda"; if
 Andrei makes allegations about Tango, they are obligated to
 either defend design decisions or accept recommendations as feasible and
 beneficial.  I think they've been doing a very good job of both given 
 the
 circumstances.  Are people not noticing this?
 I don't recall ever seeing Andrei admit he is wrong without some
 equivication or deflection. If he does, it's often hidden in some sorty
 joke or distraction. He's just not a straight forward person, plain and
 simple.  Either that or he has trouble with humility.

 He's got oodles of creativity, good ideas, experience... and personal
 opinions (like many here) and maybe even a little bit of academic 
 rigour to
 back him up :). He makes tons of :O) faces. But these matter little, 
 if he
 serves his own preferences, his own interests, and his own agenda.  
 Maybe
 I got him wrong, but from what I've seen, I doubt it.

The only civilized response I can imagine to this is killfiling sender's address. It's understandable for anyone to get heated and make a few ad hominem arguments in the midst of a heated argument. But to actually sit down and build an entire web of assumptions leading to sweeping personality judgments, that's just shady.

This "shady" claim is surely an outrageous manipulation coming from someone who has recently exhibited the attributes of a troll? JJR is one of the finest and fairest people who hang around here; he's known to be a humble and caring individual, with a certain distaste for confrontation. For him to write what he did indicates a level of discontent beyond the pale, and it seems evident that this "web of assumptions" you lay at his feet is something instigated and shaped by your own hand.
 Put yourself for a minute at the receiving end of such calumny and 
 you'll understand why you ought to be ashamed of yourself.

Then please consider your own behaviour, and the "receiving end" of those upon whom you have poured scorn? From the time you had a go at Tom through to this point, it is my opinion that you have at various times exhibited the attributes of a bigot, a bully, a megalomaniac and a troll. There will be others who share this view, and those who don't: the only thing that matters right here, given the history, is your audacity to "play to the gallery" as some /injured/ party. If there's anyone who should be ashamed, it is yourself Andrei. You've been wholly effective at frittering away the wealth of respect you had commanded (from me at least), through the kind of behaviour that John described and Roberto *nailed* with his comment about your "trolling". I'm no angel, yet I'll willingly shoulder responsibility for my actions and retract or apologize sincerely where I've made a mistake. On the other hand, I've yet to see a case (as John noted) where you've shown any remorse or real corrective action for anything questionable you've been party to. In truth, I've seen you ardently follow a course in the opposite direction. You are welcome to do that, of course, but don't start playing the injured & innocent and expect to walk away in a ray of sunshine. [yes, I'll likely be hauled over the coals on this post too; yet sometimes it is better to first get the moose onto the table, and go from there]

Ehm. It's much simpler than that. The problem with calumny, as opposed to a technical argument, is that there is little meaningful defense one can put forth, particularly in a newsgroup setting. That's why in any newsgroup debate sticking to the technical argument is important, while attacking the person is just an unprofessional cheap shot. The suggested exercise of putting oneself on the receiving position of a personal attack was meant to reveal exactly this issue (not posing as a victim etc.). However contradictory a technical argument might get, it just works to follow this simple policy - stick to the technical points being made by others, and put forth technical points as well. Progress can be made and everybody can leave the discussion enriched. This has made my and others' participation to moderated newsgroup enjoyable and productive - indeed, I can't imagine myself programming in C++ or in general without the newsgroups comp.lang.c++.moderated or comp.std.c++. It looks, however, that on an unmoderated newsgroup technical ability is second to the willingness of using potshots and personal attacks in winning an argument. You've said heavy words that were uncalled for, and I hope with time you'll acquire the decency to wish you hadn't. I won't be part of this bashing contest though; it's just silly and doesn't bring any good to anybody, so I'll sign off. Andrei -- Canis latrent, sed transigo acies.
Mar 31 2007
next sibling parent Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 
 Ehm. It's much simpler than that. The problem with calumny, as opposed 
 to a technical argument, is that there is little meaningful defense one 
 can put forth, particularly in a newsgroup setting. That's why in any 
 newsgroup debate sticking to the technical argument is important, while 
 attacking the person is just an unprofessional cheap shot. The suggested 
 exercise of putting oneself on the receiving position of a personal 
 attack was meant to reveal exactly this issue (not posing as a victim 
 etc.).
 
 However contradictory a technical argument might get, it just works to 
 follow this simple policy - stick to the technical points being made by 
 others, and put forth technical points as well. Progress can be made and 
 everybody can leave the discussion enriched. This has made my and 
 others' participation to moderated newsgroup enjoyable and productive - 
 indeed, I can't imagine myself programming in C++ or in general without 
 the newsgroups comp.lang.c++.moderated or comp.std.c++. It looks, 
 however, that on an unmoderated newsgroup technical ability is second to 
 the willingness of using potshots and personal attacks in winning an 
 argument.

My wife pointed something out to me yesterday regarding all this that seems worth mentioning here. In a work environment, it is generally a bad idea to let grievances fester because doing so can sabotage productivity and lead to divisions in the group. Instead, they are typically aired in a calm and rational manner and then the team hopefully moves on unencumbered by such feelings. As you have said however, online discussions tend to do the opposite. The lack of visual cues for determining intent tends to foster misunderstandings, and physical separation gives rise to personal attacks that are left unaddressed in the interest of the conversation at hand. I suppose the difference between the two situations is that in the former, a team of employees must work together closely, while online, people can simply choose not to interact with one another, or to do so in a limited manner. Over time, I have noticed that these D newsgroups tend to behave somewhat more like a work environment than they do a typical online forum. The number of active participants is a fairly small and regular group, and many know one another fairly well all things considered. So both approaches for resolving conflict are de rigueur in different contexts, and given the type of community which seems to have developed here, I think both are to be expected based on the various participants' backgrounds, personal tastes, and perhaps their feeling of closeness to the community. In particular, I would expect more of a work-oriented response from members of the Tango team (of which both John and Kris are a part), largely because it is essentially a development team that by necessity works together in an online context, within the larger context of the D community as a whole. I do not think there is any need to comment on any of what was said directly, but I do think it would be a mistake to believe that either party feels they are acting in any but a constructive manner. In fact, I think recent events suggest that some of the more vocal participants have accepted you as a member of this community, in their own way. If not, there would be no reason to "clear the air" in such a manner, even if what was said may have been hurtful. Sean
Mar 31 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
David B. Held wrote:
 
 Now, allow me to say a thing or two.  I always thought it interesting 
 that Andrei was able to toss Latin phrases into his CUJ columns myself. 
  I assumed he had a phrasebook handy or just took Latin in school or 
 something; but the answer is much more mundane than that.  I hope he 
 will not be too upset for spoiling his "secret", but the fact of the 
 matter is that Romanian is almost indistinguishably close to Latin, 
 which is to say, for all intents and purposes, Latin is Andrei's first 
 language.

Yup. As a point of interest for those who don't know (and to drift wildly off-topic), Romanian is one of the romance languages, all of which derive from Latin by definition. I am sure that Andrei could recant a great deal more about Romanian history than I can, but the little I know is quite interesting. It's well worth looking into for anyone interested in the propagation of language, culture, etc. Sean
Mar 31 2007
prev sibling parent Lionello Lunesu <lio lunesu.remove.com> writes:
Ow man, another newsgroup gone awry. And just after I've seen Dr. 
Zimbardo on the Daily Show and read his article at
http://chronicle.com/temp/email2.php?id=jwbxNygnt4HdpKkRmXbvrhmTJPqRVmJ3

What's up with people!? Are we so desperate to categorize eachother, so 
we can use some 'predefined' reactions?

I got depressed after reading that article, and more so after reading 
your post, Kris. Sorry.

People, just expect every comment is meant to be taken in the best way 
possible (even if that wasn't the intention). Because if you don't, 
there's no end to the misunderstandings.

L.
Mar 31 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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.
Mar 29 2007
next sibling parent reply kris <foo bar.com> writes:
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?
Mar 29 2007
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
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
Mar 29 2007
parent kris <foo bar.com> writes:
Derek Parnell wrote:
 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'

That's fine. One is invoked before two, before three. No other expectations are in place
    
 
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.

That's cool, Derek. The only expectation is that one is called 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?

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

I would hope that would always pass, since without it, plain old call-chaining would not function dependably (ingoring all the a, b, and c's) Thanks for fleshing this out like you have
Mar 29 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
kris wrote:
 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?

three(two(one(x,a),b),c) The order of evaluation of x, a, b, and c is undefined, even though one is always called before two, and two is always called before 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() ?

Yes, that is equivalent to: three(two(one(x))). The problem comes when there is more than one argument.
 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?

Yes. The problem is when there are more than one arguments involved.
Mar 30 2007
parent kris <foo bar.com> writes:
Walter Bright wrote:
 kris wrote:
 
 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?

three(two(one(x,a),b),c) The order of evaluation of x, a, b, and c is undefined, even though one is always called before two, and two is always called before three.

Thank you for putting that to rest. Actually, thank you very much.
 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() ?

Yes, that is equivalent to: three(two(one(x))). The problem comes when there is more than one argument.

No problemo
 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?

Yes. The problem is when there are more than one arguments involved.

Again, thanks for this clarification. Minor suggestion: perhaps some of this might wind up in the docs? For instance, maybe a brief section on call-chaining could act as a useful vehicle to clarify these aspects? ~ Kris
Mar 30 2007
prev sibling next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
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
Mar 29 2007
next sibling parent reply kris <foo bar.com> writes:
Derek Parnell wrote:
 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.

No problemo
 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
   _

Yes, although the second newline does not exist
 However, if the inner 'opCall' is called first the screen will show ...
 
   Please enter your name: Hello, _

That's where the tricky example is fooling everyone. In short, the example operates "correctly" (at least, on Win32) regardless of which way the execution occurs. In other words, it is not dependent upon execution order. It makes no assumptions there whatsoever ;)
 
 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.

That may be so, but as long as call-chaining operates in a left to right order, Tango doesn't care about which order parameters are evaluated. Again, the example is leading everyone on a wild goose chase. If there's one thing I'll regret from this NG, it will be posting that trivial bit of mind-bending
 
 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;

And I would generally do Cout ("Please enter your name: ") (); auto name = Cin.get; Cout ("Hello, ")(name) (); Which amounts to the same thing, I expect?
 That is of course, if I'd really used stdin/stdout in that manner at all
 <G>

Bingo! It blows me away just how much attention the lowly console is recieving :)
Mar 30 2007
parent Georg Wrede <georg nospam.org> writes:
Kris wrote:

 Bingo! It blows me away just how much attention the lowly console is 
 recieving :)

It should. Before C++ introduced cin and cout, most console I/O was [by the average programmer] written with routines specifically reading from the keyboard and writing to the screen, in DOS and Windows. Even for "line oriented programs". In Unix, the I/O has always been a more general concept (we all know, but I'm writing this for others to read too), and I/O redirecting is more the rule than the exception with non-GUI apps. Therefore, text-in-text-out programs should always use the standard handles. Only when very specific circumstances exist, one may do "direct 'console I/O' or the like". As a matter of fact, I don't remember any such apps or situations off-hand, although they certainly may exist. We need this compatibility with "the Unix way" for two reasons: most programmers have to write D for both platforms (and actually "the Unix way" should be called "the non-Windows way"). The second (and admittedly more controversial) being, IMHO Vista is the last Windows version to come out of Redmond. The next will be based on *nix, one way or another. Just like Apple did. (Please everybody, counterarguments to this should be in some new thread, not in this one.) This is not merely a convenience in Unix, it is _the_ fundamental concept of the operating system. Everything is text files, and every [non-GUI] program's I/O is redirectable. Adherence to this has brought us with vastly simpler debugging, protocols, and utility of existing programs in ways not envisioned by the original programmer. And this is the precise reason why one can, using only Telnet (a trivial terminal emulator), directly interact with a mail server, or get web pages, even when one's computer does no graphics. As an example, my first stab at the plastics processing software was a quick-and-dirty version that showed the temperatures and settings on the console. I did some cursor addressing and coloring of the output, but as I was on Linux, it all was to stdout. The output was only almost what I wanted, so I ran the program simply redirecting its output to a file myprog > myfile Looking at the file I could see the individual terminal escapes and immediately found my cursor addressing bug. Later I wanted to see a lot of live data for temperature algorithm debugging and signal to noise ratio data. So I created some text output which I sent to stderr. Then I invoked the program like myprog 2>/dev/tty7 and I could follow the debug output in real time, without messing up the normal display. Later it turned out I wanted historical data, so I invoked the program as myprog 2> mylog and when it turned out that the new machine operator was computer illiterate, I wrote a startup script #!/bin/bash myprog 2>> mylog ...I could go on and on, but the idea is clear here: if I had originally written the program to read the keyboard directly and write to the screen buffer, I would have had a lot of work to do to accomplish this all. Incidentally, now I have both logging and a real time display just by at any time opening a second session and writing tail -f mylog ---- To recap, the proper default way to do I/O is to use the pre-existing filehandles for all non-binary data. Tango should have the routines and documentation such that it steer the programmer to do just that.
Mar 31 2007
prev sibling next sibling parent Roberto Mariottini <rmariottini mail.com> writes:
Derek Parnell wrote:
[...]
 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
   _

Right, but you are assuming a flush() call that is missing (see below).
 
 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
   _

No, you are again assuming a flush() call. As Kris explained, the example works only because of buffering: no flush() is called, so the "Hello, " string stays in the output buffer until the Cin.get() call flushes it. The example works with every order of evaluation, if and only if the output buffer length is equal or greater than the length of the string "Hello, ".
 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;

This flush() call is unneeded.
     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>

<G> Ciao
Mar 30 2007
prev sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Derek Parnell wrote:
 On Thu, 29 Mar 2007 22:40:34 -0700, Walter Bright wrote:
 
 kris wrote:
    Cout.opCall("Hello, ").opCall(Cin.get);

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>

I'd do something like... import tango .io .Console ; import tango .io .Stdout ; char[] userName ; Stdout ("Please enter your name: "c) .flush ; userName = Cin.get(); Stdout .formatln("Hello, {}.", userName) .flush ; That's closer to my usage patterns with Tango. (I'm a bigger fan of Stdout than Cout in most cases, partly because I use plenty of format strings.) -- Chris Nicholson-Sauls
Mar 29 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
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.

Right. Out of curiosity, does the ">>" operator have higher precedence than the "<<" operator in C++? I know I've seen examples there where the operators were mixed on one line, and it seems like they should otherwise work just like the above.
 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'm still undecided whether this would be a good thing. It's more predictable, certainly, but it isn't clear whether that predictability is worth the loss of efficiency. I don't suppose the compiler could somehow detect situations where such dependencies exist and only prevent optimizations for those?
 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.

Certainly. About the only thing we'll need to resolve is that Stdout( "a", "b" ) currently prints "a, b" (ie. the default formatter output is comma delimited), and the lack of consistency here would be confusing. Sean
Mar 30 2007
next sibling parent kris <foo bar.com> writes:
Sean Kelly wrote:
 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.

Right. Out of curiosity, does the ">>" operator have higher precedence than the "<<" operator in C++? I know I've seen examples there where the operators were mixed on one line, and it seems like they should otherwise work just like the above.
 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'm still undecided whether this would be a good thing. It's more predictable, certainly, but it isn't clear whether that predictability is worth the loss of efficiency. I don't suppose the compiler could somehow detect situations where such dependencies exist and only prevent optimizations for those?

FWIW: I don't believe that it is necessary to cement order of eval for arguments in the general case. There are undoubtedly good reasons for the variety of "call protocols" (argument passing), and fixing the order of eval would certainly have an effect upon their efficiency. As long as call-chaining is supported in the intuitive manner that it currently is, I would say the rest of the concerns are probably "implementation dependent" 2c
 
 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.

Certainly. About the only thing we'll need to resolve is that Stdout( "a", "b" ) currently prints "a, b" (ie. the default formatter output is comma delimited), and the lack of consistency here would be confusing. Sean

Mar 30 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Out of curiosity, does the ">>" operator have higher precedence 
 than the "<<" operator in C++?

No, they are the same.
Mar 30 2007
prev sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
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?

What I meant to say was that in 'Cout ("Hello, ") (Cin.get);' there's no guarantee that Cin.get will evaluate before Cout("hello, "). (though it _is_ guaranteed that sending that value to the output buffer will happen after the string gets sent) Just because "Hello, " is somewhat unlikely to fill up the output buffer right after a flush (as it was in the code sample in question) doesn't mean it's okay to first output that and *then* ask for the name to put after it. At least, IMHO.
Mar 29 2007
next sibling parent kris <foo bar.com> writes:
Frits van Bommel 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?

What I meant to say was that in 'Cout ("Hello, ") (Cin.get);' there's no guarantee that Cin.get will evaluate before Cout("hello, "). (though it _is_ guaranteed that sending that value to the output buffer will happen after the string gets sent) Just because "Hello, " is somewhat unlikely to fill up the output buffer right after a flush (as it was in the code sample in question) doesn't mean it's okay to first output that and *then* ask for the name to put after it. At least, IMHO.

Yes, I agree. Call chaining is always left to right, though; it's just not written down in the spec at this time (along with several other things?) I don't think anyone ever said that it was good practice to rely on some implementation idiom. The fact that this operates is merely interesting, and not something to start a war over ;-)
Mar 29 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
Frits van Bommel 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?

What I meant to say was that in 'Cout ("Hello, ") (Cin.get);' there's no guarantee that Cin.get will evaluate before Cout("hello, "). (though it _is_ guaranteed that sending that value to the output buffer will happen after the string gets sent)

Oh right, this makes perfect sense. In my original post I had meant the order with respect to call chaining, not that Cin.get aberration ;-)
 Just because "Hello, " is somewhat unlikely to fill up the output buffer 
 right after a flush (as it was in the code sample in question) doesn't 
 mean it's okay to first output that and *then* ask for the name to put 
 after it. At least, IMHO.

Agreed. Sean
Mar 29 2007
prev sibling next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
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. Perhaps an aggressive inlining mechanism could violate this?

Frits has provided the exact explanation. What you say would be true in the case: Cout("Hello, ", Cin.get); Then it's clear that Cout can't possibly be called before Cin.get returned. So variadics are the best solution in this case too :o). Andrei
Mar 29 2007
prev sibling parent James Dennett <jdennett acm.org> writes:
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
Mar 29 2007
prev sibling parent reply kris <foo bar.com> writes:
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. It's funny this does not happen exactly because of buffering, but the program has no control over the buffering so it should assume flushing could happen at any time. So the correct code is: auto name = Cin.get; Cout("Hello, ")(name);

Well aware of that, thanks. BTW: evaluation order has been clarified before, on a similar topic.
Mar 29 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris 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. It's funny this does not happen exactly because of buffering, but the program has no control over the buffering so it should assume flushing could happen at any time. So the correct code is: auto name = Cin.get; Cout("Hello, ")(name);

Well aware of that, thanks. BTW: evaluation order has been clarified before, on a similar topic.

Is that clarification identical with the one posted by Frits? Andreo
Mar 29 2007
parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris 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. It's funny this does not happen exactly because of buffering, but the program has no control over the buffering so it should assume flushing could happen at any time. So the correct code is: auto name = Cin.get; Cout("Hello, ")(name);

Well aware of that, thanks. BTW: evaluation order has been clarified before, on a similar topic.

Is that clarification identical with the one posted by Frits? Andreo

Walter clarified, a long time ago, that D would evaluate chained-calls from left to right. I suggest you ask Walter?
Mar 29 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris 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. It's funny this does not happen exactly because of buffering, but the program has no control over the buffering so it should assume flushing could happen at any time. So the correct code is: auto name = Cin.get; Cout("Hello, ")(name);

Well aware of that, thanks. BTW: evaluation order has been clarified before, on a similar topic.

Is that clarification identical with the one posted by Frits? Andreo

Walter clarified, a long time ago, that D would evaluate chained-calls from left to right. I suggest you ask Walter?

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. Andrei
Mar 29 2007
next sibling parent kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 Walter clarified, a long time ago, that D would evaluate chained-calls 
 from left to right. I suggest you ask Walter?

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

There are apparently a number of things that haven't made it into the "formal" language definition doc. We've simply clarified such assertions, with Walter, along the way since there's only so much one person can do (and only so much whining one person will put up with). It would be great to have a completed language definition, but sometimes you have to accept a virtual handshake instead
Mar 29 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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.
Mar 29 2007
parent reply Sean Kelly <sean f4.ca> writes:
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
Mar 29 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
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.
Mar 29 2007
parent reply Sean Kelly <sean f4.ca> writes:
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
Mar 29 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
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.
Mar 29 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
kris wrote:
 On Win32, the difference is very much larger. As noted before, several 
 times faster.

I suspect that much of the slowness difference is from using C's fputs, along with the need to append a 0 to use fputs. std.stdio.readln will also automatically convert to char[] if the stream is in wide character mode (as will all the phobos stdio functions). This test is inlined and fast under Windows, but is a function call under Linux which will hurt performance significantly.
 If you mean something that you've written, that could presumeably be 
 rectified by adding the isatty() test Walter had mentioned before. That 
 has not been added to tango.io since (a) it would likely make programs 
 behave differently depending on whether they were redirected or not. 
 It's not yet clear whether that is an appropriate specialization, as 
 default behaviour, and (b) there has been no ticket issued for it
 
 Again, please submit a ticket so we don't forget about that detail. We'd 
 be interested to hear if folk think the "isatty() test" should be 
 default behaviour, or would perhaps lead to corner-case issues instead

Using isatty() to switch between line and block buffered I/O access is routine when using C's stdio, and in fact is relied upon in DMC's internal implementation of buffering. It's been this way for 25 years, every C stdio implementation I've heard of uses it, and I've never heard a complaint about it.
Mar 28 2007
parent kris <foo bar.com> writes:
Walter Bright wrote:
 kris wrote:
 
 On Win32, the difference is very much larger. As noted before, several 
 times faster.

I suspect that much of the slowness difference is from using C's fputs, along with the need to append a 0 to use fputs.

Okay. Oh, seemingly dout.write() has some io-synch problems when used in this manner?
 std.stdio.readln will also automatically convert to char[] if the stream 
 is in wide character mode (as will all the phobos stdio functions). This 
 test is inlined and fast under Windows, but is a function call under 
 Linux which will hurt performance significantly.

Well, phobos is running as fast as perl under linux, so perhaps it doesn't seem to be much of an issue there? Under Win32, tango.io seems to leave everything else in the dust. Seems kinda obvious why that is, when you look at what the "benchmark" is really testing? To me, it's likely spending most of its time constructing each line, so it's really not an IO test per se? Tango takes an alternate approach to such tasks, which would explains why it is so fast under Win32. What surprises us is that tango.io is almost sedate on linux by comparison. I can't explain that right now, but suspect it may have something to do with file locks, or something :)
 If you mean something that you've written, that could presumeably be 
 rectified by adding the isatty() test Walter had mentioned before. 
 That has not been added to tango.io since (a) it would likely make 
 programs behave differently depending on whether they were redirected 
 or not. It's not yet clear whether that is an appropriate 
 specialization, as default behaviour, and (b) there has been no ticket 
 issued for it

 Again, please submit a ticket so we don't forget about that detail. 
 We'd be interested to hear if folk think the "isatty() test" should be 
 default behaviour, or would perhaps lead to corner-case issues instead

Using isatty() to switch between line and block buffered I/O access is routine when using C's stdio, and in fact is relied upon in DMC's internal implementation of buffering. It's been this way for 25 years, every C stdio implementation I've heard of uses it, and I've never heard a complaint about it.

That's useful input ... thanks. It was noted that a program used both as a console process and a child process might behave differently, since flush would be automatic on the console yet not always so for the child (with redirected handles) ?
Mar 28 2007
prev sibling next sibling parent John Reimer <terminal.node gmail.com> writes:
 It has been said before, but is probably worth repeating:
 
 - Tango is not a phobos clone. Nor is it explicitly designed to be 
 compatible with phobos; sometimes it is worthwhile taking a different 
 approach. Turns out that phobos can be run alongside tango in many 
 situations.
 
 - Tango is for D programmers; not C programmers.
 
 - Tango, as a rule, is intended to be flexible, modular, efficient and 
 practical. The goal is to provide D with an exceptional library, and we 
 reserve the right to break a few eggs along the way ;)

Totally agree!
Mar 28 2007
prev sibling next sibling parent John Reimer <terminal.node gmail.com> writes:
On Thu, 29 Mar 2007 15:19:40 +0900, Bill Baxter wrote:

 kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:

 - Tango is for D programmers; not C programmers.

D programmers sometimes like to call 3rd party code written in other languages, and pretty much any interop in D has to happen via C compatibility. E.g. pyD. So I'm guessing if my D code calls on some Python code that prints to the console that somewhere down the line that eventually ends up on C's stdout. I could be wrong, but at least that's why I *think* Andrei and Walter keep saying that C compatibility is important.

It's only important for those that are determined to use it, perhaps because of persistant programming style or unrelenting attachment to C++ methods (where C and C++ seemed to keep an unholy matrimony). I'd hate to see a D library infected with a C compatibility requirement -- support for ugly C design decisions would most certainly taint Tango for the worse. I believe Tango was made to help D step beyond that. Tango based projects would turn into a big mess if some tasks were mixed haphazardly with C code for compatibilities sake. The decision, I believe, to make Tango distinct is a very good one. This is what makes Tango original, performant, unencumbered, and readable. At the same time, Tango certainly doesn't prevent anyone from using C API's, if they so choose. But I think it would be wiser and cleaner to separate such code into "well-marked" units and leave Tango to do what it does best. I've always had a distinct dislike for C++ source that mixed "old" C API's with new "C++" ones. I'd love to see the day when D is completely unleashed, that is, literally unfettered from it's C dependency (snn.lib and libc.a no more -- ok that's excentric, but I can wish. :D ) Tango certainly has its own ideology that attracts certain types of programmers. For myself, I hope Tango continues to steer far clear of anything resembling C++/C mixing and matching. C is certainly accessible through D, but use it wisely and certainly don't force a library design to be crippled by C compatibility. Just an opinion. -JJR
Mar 29 2007
prev sibling next sibling parent torhu <fake address.dude> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 On my machine, Tango does 4.3 seconds and the following phobos program 
 (with Walter's readln) does 5.4 seconds:
 
 #!/usr/bin/env rundmd
 import std.stdio;
 
 void main() {
    char[] line;
    while (readln(line)) {
      write(line);
    }
 }
 
 where write is a function that isn't yet in phobos, of the following 
 implementation:
 
 size_t write(char[] s) {
    return fwrite(s.ptr, 1, s.length, stdout);
 }

On my Windows machine, using fwrite like that makes phobos twice as fast as the version using fputs. But tango is still twice as fast as that.
Mar 29 2007
prev sibling parent John Reimer <terminal.node gmail.com> writes:
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
Mar 29 2007
prev sibling parent Dave <Dave_member pathlink.com> writes:
kris wrote:
 
 FWIW: if some of those "Language Shootout" tests are IO-bound, perhaps 
 tango.io might help? Can't imagine they'd apply that as a "language" 
 test, but stranger things have happened before.
 

FWIW: I think it's worth a lot <g> If D could come out convincingly on top for common I/O bound stuff, and do so with a "more modern" API than a C stdio clone, I would think that would be worth something to quite a few people. IIRC, making the I/O as fast as C was time-consuming for quite a few of the current programs -- more so than even the GC. Even for the programs where I/O is not a "bottleneck" (but where I/O was involved), the faster implementation would still cut the overall time a bit anyway. Alas, since Tango is not part of the std. distribution, I think they'd be very reluctant to add it - not only because of the extra workload for them, but simply because it's not considered std. for D. OTOH, they did add gmp4d for the pidigits test, but I think that was because it was easy to add via CVS, use through an altered command-line and there wasn't an alternative. If the same could be done for Tango, hmmm, perhaps... - Dave
Apr 01 2007