digitalmars.D - stdio line-streaming revisited
- kris (72/72) Mar 28 2007 Last week there were a series of posts regarding some optimized code
- Andrei Alexandrescu (See Website For Email) (28/35) Mar 28 2007 [snip]
- kris (13/52) Mar 28 2007 On Win32, the difference is very much larger. As noted before, several
- Andrei Alexandrescu (See Website For Email) (24/61) Mar 28 2007 If I understand things correctly, it looks like the hope is to derive
- kris (31/106) Mar 28 2007 Nope. That's not the case at all. The expectation (or 'hope', if you
- Bill Baxter (19/21) Mar 28 2007 D programmers sometimes like to call 3rd party code written in other
- Andrei Alexandrescu (See Website For Email) (13/35) Mar 28 2007 Exactly. I'm hoping to save the D community from the iostreams mess,
- kris (7/20) Mar 29 2007 Save the D community? The sky is not falling; no need for you to save
- Dave (72/86) Apr 01 2007 any interop in D has to happen via C compatibility. E.g. pyD. So I'm g...
- Sean Kelly (10/19) Apr 01 2007 The compiler runtime is a logically separate entity in Tango. Some
- kris (3/31) Mar 28 2007 That post noted the feasibility of hooking C into tango.io, which is a
- John Reimer (26/38) Mar 29 2007 It's only important for those that are determined to use it, perhaps
- Bill Baxter (7/27) Mar 29 2007 I believe the current discussion is only about under-the-hood
- Andrei Alexandrescu (See Website For Email) (4/32) Mar 29 2007 That's great, however the interface has a problem too: it does not
- Sean Kelly (3/13) Mar 29 2007 Which interface? And are you talking about input or output?
- Andrei Alexandrescu (See Website For Email) (5/18) Mar 29 2007 Cout. The thing is that the design using the syntax Cout(a)(b) is
- Frits van Bommel (4/22) Mar 29 2007 Of course that could easily be fixed if either struct destructors or
- Andrei Alexandrescu (See Website For Email) (4/27) Mar 29 2007 Struct destructors will be added, but typesafe variadics are already in....
- Frits van Bommel (10/29) Mar 29 2007 I dislike typesafe variadics for I/O, mainly because it'll instantiate a...
- Andrei Alexandrescu (See Website For Email) (8/38) Mar 29 2007 I think this is not a problem (for a well-written library) as those many...
- Sean Kelly (8/26) Mar 29 2007 Just making sure we were on the same page. In my experience, raw output...
- Andrei Alexandrescu (See Website For Email) (10/36) Mar 29 2007 I don't think so. For example, I have a multithreaded program that
- Sean Kelly (5/42) Mar 29 2007 This sounds to me like logging output, which is exactly what I
- Andrei Alexandrescu (See Website For Email) (29/73) Mar 29 2007 Oh, I thought you meant logging as just auxiliary informative message as...
- Sean Kelly (28/41) Mar 29 2007 Oh right, this is what tie() is for with iostreams. It's come up before...
- Andrei Alexandrescu (See Website For Email) (10/57) Mar 29 2007 I understand. Probably making the mode fast/incompatible by default is
- kris (45/70) Mar 29 2007 I'm not entirely sure what causes the sky to fall down for Andrei here?
- Sean Kelly (7/34) Mar 29 2007 No, that's exactly right. And I think the current need for a .flush is
- Andrei Alexandrescu (See Website For Email) (23/142) Mar 28 2007 I think we're not on the same page here. What I'm saying is that, unless...
- kris (5/7) Mar 29 2007 We ask ppl to create tickets so that we can track and schedule the
- Sean Kelly (14/122) Mar 29 2007 I must be missing something. Why is the following not acceptable?
- kris (9/20) Mar 29 2007 There used to be a tango/example like this variation:
- Roberto Mariottini (4/28) Mar 29 2007 I think I'll take a look at Tango.
- Lars Ivar Igesund (9/39) Mar 29 2007 The website over at http://www.dsource.org/projects/tango has various
- Andrei Alexandrescu (See Website For Email) (11/35) Mar 29 2007 Ah, also, the last line is translated into:
- Sean Kelly (8/40) Mar 29 2007 We discussed this a long time ago and came to the conclusion that while
- Frits van Bommel (26/51) Mar 29 2007 Actually if you go a bit lower-level (explicit 'this'), that line is
- Andrei Alexandrescu (See Website For Email) (3/57) Mar 29 2007 Exactly so, thanks for the clear explanation.
- kris (11/14) Mar 29 2007 Absolutely agree. Yet, without wishing to go off on a tangent, we're
- jcc7 (16/29) Mar 29 2007 does that, but he sure is effective at it :)
- kris (4/42) Mar 29 2007 If I recall correctly, it started off as a complaint about the use of
- jcc7 (11/53) Mar 29 2007 I think this is what you're looking for:
- Andrei Alexandrescu (See Website For Email) (9/57) Mar 29 2007 After a summary read I saw this:
- jcc7 (11/69) Mar 29 2007 (such
- kris (5/15) Mar 29 2007 Nobody claimed that it "guaranteedly" (whatever that means) does in the
- Andrei Alexandrescu (See Website For Email) (13/31) Mar 29 2007 You wrote this:
- kris (5/28) Mar 29 2007 It operates just fine due to the underlying mechanisms, so "point blank"...
- Frits van Bommel (29/75) Mar 29 2007 Actually, we *were* talking about parameter evaluation-order in this
- kris (4/11) Mar 29 2007 Then I suggest you ask Walter? Please do so on a different thread, since...
- Andrei Alexandrescu (See Website For Email) (9/14) Mar 29 2007 My understanding is that it was brought up by yourself in an attempt to
- kris (19/36) Mar 29 2007 Then I politely suggest you are either badly confused, being entirely
- James Dennett (8/40) Mar 29 2007 There are two arguments to the second opCall. One is
- kris (8/27) Mar 29 2007 I think you'll find that call-chaining does not operate in that manner,
-
James Dennett
(8/35)
Mar 29 2007
Walter's post in this thread (
) - kris (4/47) Mar 29 2007 Simply because that's what Walter led us to believe a long time ago,
- James Dennett (13/66) Mar 29 2007 I doubt that Walter lead you to believe that the order of
- kris (31/109) Mar 29 2007 *sigh*
- James Dennett (30/150) Mar 30 2007 If you mean the same thing that I do by "call chaining",
- kris (9/18) Mar 30 2007 The example was known to be a tricky one, and the reaction to it was
- James Dennett (28/80) Mar 29 2007 I don't want to use a lot of bandwidth to discuss this,
- Roberto Mariottini (8/20) Mar 30 2007 I agree that this is "flaming".
- kris (14/26) Mar 30 2007 That's cool, James.
- Andrei Alexandrescu (See Website For Email) (9/82) Mar 30 2007 The only civilized response I can imagine to this is killfiling sender's...
- kris (32/74) Mar 30 2007 This "shady" claim is surely an outrageous manipulation coming from
- Andrei Alexandrescu (See Website For Email) (26/115) Mar 31 2007 Ehm. It's much simpler than that. The problem with calumny, as opposed
- Sean Kelly (37/57) Mar 31 2007 My wife pointed something out to me yesterday regarding all this that
- Sean Kelly (8/17) Mar 31 2007 Yup. As a point of interest for those who don't know (and to drift
- Lionello Lunesu (11/11) Mar 31 2007 Ow man, another newsgroup gone awry. And just after I've seen Dr.
- Walter Bright (27/28) Mar 29 2007 Given:
- kris (19/54) Mar 29 2007 Thank you for that clarification, Walter.
- Derek Parnell (22/60) Mar 29 2007 I'm guessing ...
- kris (7/77) Mar 29 2007 That's fine. One is invoked before two, before three. No other
- Walter Bright (7/69) Mar 30 2007 three(two(one(x,a),b),c)
- kris (7/89) Mar 30 2007 No problemo
- Derek Parnell (53/71) Mar 29 2007 Now that makes sense. I can see now why the W&A show is concerned. In fa...
- kris (19/109) Mar 30 2007 Yes, although the second newline does not exist
- Georg Wrede (59/61) Mar 31 2007 It should.
- Roberto Mariottini (13/69) Mar 30 2007 Right, but you are assuming a flush() call that is missing (see below).
- Chris Nicholson-Sauls (11/102) Mar 29 2007 I'd do something like...
- Sean Kelly (14/39) Mar 30 2007 Right. Out of curiosity, does the ">>" operator have higher precedence
- kris (8/61) Mar 30 2007 FWIW: I don't believe that it is necessary to cement order of eval for
- Walter Bright (2/4) Mar 30 2007 No, they are the same.
- Frits van Bommel (9/14) Mar 29 2007 What I meant to say was that in 'Cout ("Hello, ") (Cin.get);' there's no...
- kris (6/25) Mar 29 2007 Yes, I agree. Call chaining is always left to right, though; it's just
- Sean Kelly (5/20) Mar 29 2007 Oh right, this makes perfect sense. In my original post I had meant the...
- John Reimer (5/8) Mar 29 2007 Oh, Manfred is back... he certainly didn't leave for good; he just appea...
- Andrei Alexandrescu (See Website For Email) (7/47) Mar 29 2007 Frits has provided the exact explanation. What you say would be true in
- James Dennett (10/49) Mar 29 2007 All you get from that is a partial order that says
- kris (3/45) Mar 29 2007 Well aware of that, thanks. BTW: evaluation order has been clarified
- Andrei Alexandrescu (See Website For Email) (3/49) Mar 29 2007 Is that clarification identical with the one posted by Frits?
- kris (3/60) Mar 29 2007 Walter clarified, a long time ago, that D would evaluate chained-calls
- Andrei Alexandrescu (See Website For Email) (5/66) Mar 29 2007 As long as it's not in the language definition, you can't count on it. I...
- kris (7/17) Mar 29 2007 There are apparently a number of things that haven't made it into the
- Walter Bright (4/7) Mar 29 2007 It's problematical to define the order of function argument evaluation,
- Sean Kelly (5/14) Mar 29 2007 It sounds like parameter evaluation order is being confused with
- Walter Bright (7/22) Mar 29 2007 Let's say you have:
- Sean Kelly (5/32) Mar 29 2007 Okay, right. This is what I'd expect. Though I suppose it could indeed...
- Walter Bright (3/17) Mar 29 2007 One reason to disallow implementation flexibility on this is to improve
- John Reimer (1/13) Mar 28 2007 Totally agree!
- Walter Bright (12/24) Mar 28 2007 I suspect that much of the slowness difference is from using C's fputs,
- kris (17/47) Mar 28 2007 Okay. Oh, seemingly dout.write() has some io-synch problems when used in...
- torhu (3/22) Mar 29 2007 On my Windows machine, using fwrite like that makes phobos twice as fast...
-
Dave
(13/18)
Apr 01 2007
FWIW: I think it's worth a lot
If D could come out convincingly on t...
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 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
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: 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
Andrei Alexandrescu (See Website For Email) wrote:kris wrote:On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.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: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 goneAlso, 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
kris wrote:Andrei Alexandrescu (See Website For Email) wrote: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.kris wrote:On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.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: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.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 insteadI 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
Andrei Alexandrescu (See Website For Email) wrote:kris wrote: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 versionAndrei Alexandrescu (See Website For Email) wrote: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.kris wrote:On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.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: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" approachOnly 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 :)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.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 behaviourThe 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 ;), 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 insteadI 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.
Mar 28 2007
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
Bill Baxter wrote:kris wrote:Yes.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.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
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 anythingIt'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
Andrei Alexandrescu (See Website For Email) wrote:Bill Baxter wrote: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 Well, it *all* ends up using file descriptor 0 on POSIX systems.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 muchPlease 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.that's why I *think* Andrei and Walter keep saying that C compatibility is important.Yes.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.Andrei -- by "compatibility" does that mean if I rebind stdio/stdout to something different thatExactly. I'm hoping to save the D community from the iostreams mess, which requires theprogrammer 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 ahigh-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).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, - DaveAndrei
Apr 01 2007
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
Bill Baxter wrote:kris wrote:That post noted the feasibility of hooking C into tango.io, which is a potentially realistic optionAndrei 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
On Thu, 29 Mar 2007 15:19:40 +0900, Bill Baxter wrote:kris wrote: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. -JJRAndrei 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.
Mar 29 2007
John Reimer wrote:On Thu, 29 Mar 2007 15:19:40 +0900, 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. --bbkris wrote: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.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.
Mar 29 2007
Bill Baxter wrote:John Reimer wrote:That's great, however the interface has a problem too: it does not produce atomic strings in multithreaded programs. AndreiOn Thu, 29 Mar 2007 15:19:40 +0900, 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.kris wrote: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.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.
Mar 29 2007
Andrei Alexandrescu (See Website For Email) wrote:Bill Baxter wrote:Which interface? And are you talking about input or output? SeanI 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.
Mar 29 2007
Sean Kelly wrote:Andrei Alexandrescu (See Website For Email) wrote: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. AndreiBill Baxter wrote:Which interface? And are you talking about input or output?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.
Mar 29 2007
Andrei Alexandrescu (See Website For Email) wrote:Sean Kelly wrote: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.Andrei Alexandrescu (See Website For Email) wrote: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.Bill Baxter wrote:Which interface? And are you talking about input or output?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.
Mar 29 2007
Frits van Bommel wrote:Andrei Alexandrescu (See Website For Email) wrote:Struct destructors will be added, but typesafe variadics are already in. I think Cout(a, b) is the most natural interface to offer. AndreiSean Kelly wrote: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.Andrei Alexandrescu (See Website For Email) wrote: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.Bill Baxter wrote:Which interface? And are you talking about input or output?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.
Mar 29 2007
Andrei Alexandrescu (See Website For Email) wrote:Frits van Bommel wrote: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.Andrei Alexandrescu (See Website For Email) wrote:Struct destructors will be added, but typesafe variadics are already in. I think Cout(a, b) is the most natural interface to offer.Sean Kelly wrote: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.Andrei Alexandrescu (See Website For Email) wrote: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.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?
Mar 29 2007
Frits van Bommel wrote:Andrei Alexandrescu (See Website For Email) wrote: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.Frits van Bommel wrote: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.Andrei Alexandrescu (See Website For Email) wrote:Struct destructors will be added, but typesafe variadics are already in. I think Cout(a, b) is the most natural interface to offer.Sean Kelly wrote: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.Andrei Alexandrescu (See Website For Email) wrote: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.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?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
Andrei Alexandrescu (See Website For Email) wrote:Sean Kelly wrote: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? SeanAndrei Alexandrescu (See Website For Email) wrote: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.Bill Baxter wrote:Which interface? And are you talking about input or output?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.
Mar 29 2007
Sean Kelly wrote:Andrei Alexandrescu (See Website For Email) wrote: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. AndreiSean Kelly wrote: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?Andrei Alexandrescu (See Website For Email) wrote: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.Bill Baxter wrote:Which interface? And are you talking about input or output?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.
Mar 29 2007
Andrei Alexandrescu (See Website For Email) wrote:Sean Kelly wrote: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? SeanAndrei Alexandrescu (See Website For Email) wrote: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.Sean Kelly wrote: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?Andrei Alexandrescu (See Website For Email) wrote: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.Bill Baxter wrote:Which interface? And are you talking about input or output?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.
Mar 29 2007
Sean Kelly wrote:Andrei Alexandrescu (See Website For Email) wrote: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.Sean Kelly wrote:This sounds to me like logging output, which is exactly what I described.Andrei Alexandrescu (See Website For Email) wrote: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.Sean Kelly wrote: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?Andrei Alexandrescu (See Website For Email) wrote: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.Bill Baxter wrote:Which interface? And are you talking about input or output?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.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
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
Sean Kelly wrote:Andrei Alexandrescu (See Website For Email) wrote: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). AndreiSo 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.
Mar 29 2007
Sean Kelly wrote:Andrei Alexandrescu (See Website For Email) wrote: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?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.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.(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.
Mar 29 2007
kris wrote:Sean Kelly wrote: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. SeanAndrei Alexandrescu (See Website For Email) wrote: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?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.
Mar 29 2007
kris wrote:Andrei Alexandrescu (See Website For Email) wrote: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."kris wrote: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 versionAndrei Alexandrescu (See Website For Email) wrote: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.kris wrote:On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.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: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" approachThis 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.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 :)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.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 behaviourWhat Win32 primitives does tango use?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., 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 insteadI 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.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
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
Andrei Alexandrescu (See Website For Email) wrote:kris wrote: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 )( "!" ); }Andrei Alexandrescu (See Website For Email) wrote: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."kris wrote: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 versionAndrei Alexandrescu (See Website For Email) wrote: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.kris wrote:On Win32, the difference is very much larger. As noted before, several times faster. Those benefits will likely translate to linux going forward.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: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" approachThis 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! $ _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 :)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.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 behaviourI 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
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
kris wrote:Sean Kelly wrote: [snip]I think I'll take a look at Tango. Is there a tutorial, or better an examples library? CiaoI 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
Roberto Mariottini wrote:kris wrote: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 TangoSean Kelly wrote: [snip]I think I'll take a look at Tango. Is there a tutorial, or better an examples library? CiaoI 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
kris wrote:Sean Kelly wrote: [snip]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); AndreiI 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
Andrei Alexandrescu (See Website For Email) wrote:kris wrote: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? SeanSean Kelly wrote: [snip]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.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
Sean Kelly wrote:Andrei Alexandrescu (See Website For Email) wrote: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.kris wrote: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?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.
Mar 29 2007
Frits van Bommel wrote:Sean Kelly wrote:Exactly so, thanks for the clear explanation. AndreiAndrei Alexandrescu (See Website For Email) wrote: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.kris wrote: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?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.
Mar 29 2007
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
== Quote from kris (foo bar.com)'s articleFrits van Bommel wrote: [snip]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. ;) jcc7What 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
Mar 29 2007
jcc7 wrote:== Quote from kris (foo bar.com)'s articleIf 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?)Frits van Bommel wrote: [snip]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. ;) jcc7What 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
Mar 29 2007
== Quote from kris (foo bar.com)'s articlejcc7 wrote:Specification. ;)== Quote from kris (foo bar.com)'s articleFrits van Bommel wrote: [snip]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 DWhat 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 heI 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.) jcc7jcc7If 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
jcc7 wrote:== Quote from kris (foo bar.com)'s articleThe information is not there.jcc7 wrote:Specification. ;)== Quote from kris (foo bar.com)'s articleFrits van Bommel wrote: [snip]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 DWhat 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 heI 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=31264jcc7If 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?)It's part of a much larger discussion: http://www.digitalmars.com/d/archives/digitalmars/D/31069.html#N31264After 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
== Quote from Andrei Alexandrescu (See Website For Email) (SeeWebsiteForEmail erdani.org)'s articlejcc7 wrote:(such== Quote from kris (foo bar.com)'s articlejcc7 wrote:== Quote from kris (foo bar.com)'s articleFrits van Bommel wrote: [snip]does that, but he sure is effective at it :) These days, I Usually just use Google and restrict it to www.digitalmars.comWhat 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 hewell whenas 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 reallyLastI 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?haystack.year? 2005?). But this might just be a needle that remains hidden in thehttp://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=31264Specification. ;)Perhaps we can just talk Walter into putting this principle in the DI think this is what you're looking for:jcc7If 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?)http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=31264orThe information is not there.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. jcc7It's part of a much larger discussion: http://www.digitalmars.com/d/archives/digitalmars/D/31069.html#N31264After 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
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
kris wrote:Andrei Alexandrescu (See Website For Email) wrote: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? AndreiAfter 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
Andrei Alexandrescu (See Website For Email) wrote:kris wrote:[snip]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 :-DNobody 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 againYou 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?
Mar 29 2007
jcc7 wrote:== Quote from kris (foo bar.com)'s articleActually, 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)jcc7 wrote:== Quote from kris (foo bar.com)'s articleFrits 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[snip] Yes, call-chaining can only evaluate left-to-right, but the parameters *passed* to the calls can be evaluated in any order.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.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...manyMoreover 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 offerbenefits, and causes a lot of headaches. But there are two different things here: order of evaluation, andoperatorprecedence. 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
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
kris wrote:Frits van Bommel wrote: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. AndreiYes, 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?
Mar 29 2007
Andrei Alexandrescu (See Website For Email) wrote:kris wrote: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 continueFrits van Bommel wrote: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.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?
Mar 29 2007
kris wrote:Andrei Alexandrescu (See Website For Email) wrote: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. -- Jameskris wrote: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).Frits van Bommel wrote: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.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?
Mar 29 2007
James Dennett wrote:kris wrote:[snip]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?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.With pleasure, James Dennett
Mar 29 2007
kris wrote:James Dennett wrote: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.kris wrote:[snip]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?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.
Mar 29 2007
James Dennett wrote:kris wrote: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.James Dennett wrote: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.kris wrote:[snip]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?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.
Mar 29 2007
kris wrote:James Dennett wrote: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:kris wrote: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.James Dennett wrote: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.kris wrote:[snip]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?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.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
James Dennett wrote:kris wrote:*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?James Dennett wrote: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.)kris wrote: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.James Dennett wrote: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.kris wrote:[snip]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?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.Did you read the message of Walter's to which I referred?NaturallyWalter wrote, today: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?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?)
Mar 29 2007
kris wrote:James Dennett wrote:Yes, that's what I was trying to do.kris wrote:*sigh* let's tease a couple of things apart, James?James Dennett wrote: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.)kris wrote: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.James Dennett wrote: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.kris wrote:[snip]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?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.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.Good; I thought I'd check.Did you read the message of Walter's to which I referred?NaturallyThe point I quoted for was the "otherwise the order is undefined", which doesn't apply to only the opCall calls.Walter wrote, today: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 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?)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
James Dennett wrote:kris wrote:[snip]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 subjectThere's a good reason why "mystery" is in the topic line ;)What is that? (Seriously.)you are welcomeI do hope that helps?It does, thank you.
Mar 30 2007
John Reimer wrote:On Thu, 29 Mar 2007 21:59:49 -0700, James Dennett wrote: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:kris wrote:I disagree with your strange assertion that Kris is flaming.Andrei Alexandrescu (See Website For Email) wrote: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. -- Jameskris wrote: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).Frits van Bommel wrote: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.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?I politely suggest you are either badly confused, being entirely disengeneous, or are drunk ;-)andyou don't appear to be the slightest bit interested in trying to understand muchandyour demeanour has been and continues to be that of someone with a hidden adgenda or with a axe to grindandIf you'll drop that attitute forthwith, I'll be happy to continueEmotions 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
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
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
John Reimer wrote:On Thu, 29 Mar 2007 21:59:49 -0700, James Dennett wrote: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. Andreikris wrote: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.Andrei Alexandrescu (See Website For Email) wrote: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. -- Jameskris wrote: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).Frits van Bommel wrote: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.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?
Mar 30 2007
Andrei Alexandrescu (See Website For Email) wrote:John Reimer wrote:[snip]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.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.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
kris wrote: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. 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.John Reimer wrote:[snip]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.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.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 31 2007
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
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
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
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
Walter Bright wrote: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?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.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 tmp1 = x.one(); auto tmp2 = tmp1.two(); auto result = tmp2.three(); Is that incorrect?
Mar 29 2007
On Thu, 29 Mar 2007 23:14:09 -0700, kris wrote:Thank you for that clarification, Walter. You note that the inner opCall will be evaluated before the outer one; how is x.one(a).two(b).three(c) tranformed?I'm guessing ... three( two( one(x, a), b), c); thus either 'x' or 'a' is done first, then the other then either 'one' or 'b', then the other then either 'two' or 'c', then the other and finally 'three'I 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 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 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 PMI 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 tmp1 = x.one(); auto tmp2 = tmp1.two(); auto result = tmp2.three(); Is that incorrect?
Mar 29 2007
Derek Parnell wrote:On Thu, 29 Mar 2007 23:14:09 -0700, kris wrote:That's fine. One is invoked before two, before three. No other expectations are in placeThank 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 cool, Derek. The only expectation is that one is called before twoI 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 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 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 haveI 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 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 tmp1 = x.one(); auto tmp2 = tmp1.two(); auto result = tmp2.three(); Is that incorrect?
Mar 29 2007
kris wrote:Walter Bright wrote: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.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?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.).Yes, that is equivalent to: three(two(one(x))). The problem comes when there is more than one argument.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. The problem is when there are more than one arguments involved.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 tmp1 = x.one(); auto tmp2 = tmp1.two(); auto result = tmp2.three(); Is that incorrect?
Mar 30 2007
Walter Bright wrote:kris wrote:Thank you for putting that to rest. Actually, thank you very much.Walter Bright wrote: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.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?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.).No problemoYes, that is equivalent to: three(two(one(x))). The problem comes when there is more than one argument.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() ?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? ~ KrisYes. The problem is when there are more than one arguments involved.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 tmp1 = x.one(); auto tmp2 = tmp1.two(); auto result = tmp2.three(); Is that incorrect?
Mar 30 2007
On Thu, 29 Mar 2007 22:40:34 -0700, Walter Bright wrote:kris wrote: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 PMCout.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.).
Mar 29 2007
Derek Parnell wrote:On Thu, 29 Mar 2007 22:40:34 -0700, Walter Bright wrote:No problemokris wrote: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.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.).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 existHowever, 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-bendingBeing 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
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 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
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
Derek Parnell wrote:On Thu, 29 Mar 2007 22:40:34 -0700, Walter Bright wrote: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-Saulskris wrote: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>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.).
Mar 29 2007
Walter Bright wrote:kris wrote: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.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.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
Sean Kelly wrote:Walter Bright wrote: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" 2ckris wrote: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.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.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
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
kris wrote:Frits van Bommel wrote: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, 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?
Mar 29 2007
Frits van Bommel wrote:kris wrote: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 ;-)Frits van Bommel wrote: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, 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?
Mar 29 2007
Frits van Bommel wrote:kris wrote: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 ;-)Frits van Bommel wrote: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)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?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
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
Sean Kelly wrote:Andrei Alexandrescu (See Website For Email) wrote: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). Andreikris wrote: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 Kelly wrote: [snip]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.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
Sean Kelly wrote:Andrei Alexandrescu (See Website For Email) wrote: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. -- Jameskris wrote: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.Sean Kelly wrote: [snip]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.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
Andrei Alexandrescu (See Website For Email) wrote:kris wrote:Well aware of that, thanks. BTW: evaluation order has been clarified before, on a similar topic.Sean Kelly wrote: [snip]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);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
kris wrote:Andrei Alexandrescu (See Website For Email) wrote:Is that clarification identical with the one posted by Frits? Andreokris wrote:Well aware of that, thanks. BTW: evaluation order has been clarified before, on a similar topic.Sean Kelly wrote: [snip]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);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
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?Andrei Alexandrescu (See Website For Email) wrote:Is that clarification identical with the one posted by Frits? Andreokris wrote:Well aware of that, thanks. BTW: evaluation order has been clarified before, on a similar topic.Sean Kelly wrote: [snip]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);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
kris 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. Andreikris wrote:Walter clarified, a long time ago, that D would evaluate chained-calls from left to right. I suggest you ask Walter?Andrei Alexandrescu (See Website For Email) wrote:Is that clarification identical with the one posted by Frits? Andreokris wrote:Well aware of that, thanks. BTW: evaluation order has been clarified before, on a similar topic.Sean Kelly wrote: [snip]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);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
Andrei Alexandrescu (See Website For Email) wrote:kris wrote: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 insteadWalter 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
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
Walter Bright wrote:Andrei Alexandrescu (See Website For Email) wrote: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? SeanAs 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
Sean Kelly wrote:Walter Bright 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.Andrei Alexandrescu (See Website For Email) wrote: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?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
Walter Bright wrote:Sean Kelly wrote: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? SeanWalter Bright 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.Andrei Alexandrescu (See Website For Email) wrote: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?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
Sean Kelly wrote:One reason to disallow implementation flexibility on this is to improve source code portability.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?
Mar 29 2007
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
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 insteadUsing 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
Walter Bright wrote:kris wrote:Okay. Oh, seemingly dout.write() has some io-synch problems when used in this manner?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.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 :)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) ?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 insteadUsing 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
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: 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
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