digitalmars.D - What is it? Syntax sugar or some stupid lazyness?
- MIcroWizard (9/9) Dec 09 2005 I've just seen in one Mango example:
- Jarrett Billingsley (25/27) Dec 09 2005 I haven't used Mango, but I bet it does this:
- John Demme (4/43) Dec 09 2005 In fact that's exactly what it does... This was originally called the
- Manfred Nowak (23/25) Dec 09 2005 I do not find it that nice, because the whisper notation restricts
- pragma (13/38) Dec 09 2005 I'd hate to have you re-explain yourself on this thread, but I simply do...
- Sean Kelly (3/12) Dec 09 2005 Agreed. Where does expression ordering ambiguity come into play here?
- Derek Parnell (9/14) Dec 09 2005 As the sequence of evaluation is not defined (in the general case), this
- Manfred Nowak (13/19) Dec 09 2005 Very true.
- Kris (24/33) Dec 09 2005 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Manfred Nowak (10/18) Dec 09 2005 No. I see your arguments as beeing totally contradictory.
- Sean Kelly (5/26) Dec 09 2005 I don't think Kris meant to imply that the comma operator was being used...
- Manfred Nowak (15/18) Dec 09 2005 Maybe, but he uses the word "expression" together with a `;' at the
- Kris (18/20) Dec 09 2005 OK ... I'm confused. Can someone /please/ explain what this supposed
- Derek Parnell (26/39) Dec 10 2005 I'm with you Kris on this too, BTW. If we assume left-to-right scanning ...
- Kris (3/45) Dec 10 2005 Thank you, Derek.
- Kris (24/54) Dec 09 2005 That's an interesting point.
- Derek Parnell (14/16) Dec 09 2005 Yes, that would clear things up nicely.
- BCS (12/28) Dec 09 2005 the order of evaluation can make a difference even without side effects
- Manfred Nowak (6/7) Dec 09 2005 very nice example :-)
- Kris (12/14) Dec 09 2005 [cue music from "Rocky", or "Jaws"]
- Walter Bright (10/34) Dec 13 2005 says...
- BCS (8/72) Dec 13 2005 oops, my bad, it was supposed to be:
- Walter Bright (3/5) Dec 13 2005 It's implicit in the grammar for AddExpression's.
- BCS (5/15) Dec 13 2005 I just reread that, and I don't see it.
- xs0 (8/27) Dec 13 2005 First, you should trust Walter when he says something :)
- BCS (8/39) Dec 13 2005 Thank you for that explanation, (I was looking in the text so I missed
- Sean Kelly (11/21) Dec 13 2005 I believe this is because expressions are evaluated left to right,
- Bruno Medeiros (17/37) Dec 09 2005 Doubtful. It is not clear in the spec, but I bet in D the operator
- Sean Kelly (7/39) Dec 09 2005 Exactly. In fact, most of this behavior is undefined in C++ for this
- Bruno Medeiros (12/14) Dec 09 2005 In fact, check the D expressions spec(
- Manfred Nowak (8/10) Dec 09 2005 [...]
- Bruno Medeiros (13/25) Dec 10 2005 I was thinking "precedence" in the general sense, that is both
- Manfred Nowak (7/9) Dec 10 2005 Look at the promise of not reordering (sub)expressions for the
- Sean Kelly (7/33) Dec 09 2005 I think it should. The C++ spec states that "postfix expressions group
- Manfred Nowak (13/15) Dec 09 2005 [...]
- Bruno Medeiros (9/29) Dec 09 2005 If the order is not defined, then each implementation would be able to
- BCS (34/37) Dec 09 2005 Ambiguous code, as described above, is illegal code. If order of evaluat...
- James Dunne (10/67) Dec 09 2005 That's complete nonsense. I don't see what you're getting at.
- Manfred Nowak (6/8) Dec 09 2005 You are right in that there is a typo. Simply suppose that it should
- Manfred Nowak (7/9) Dec 09 2005 [...]
- Kris (2/8) Dec 10 2005 And you, my friend, are quite foolish, trolling, or both.
- Manfred Nowak (6/6) Dec 10 2005 Kris wrote:
- Kris (16/22) Dec 10 2005 (*cough*) ROFL!
- Derek Parnell (12/21) Dec 10 2005 I assume by this that you hit a wall, Manfred.
- Kris (19/31) Dec 09 2005 A a;
- Manfred Nowak (21/23) Dec 09 2005 Yes.
- Kris (32/54) Dec 10 2005 Ah. Well, D doesn't operate like that. If it did, that example would res...
- Manfred Nowak (9/11) Dec 10 2005 As a member of this community created by the free will of intelligent
- BCS (11/22) Dec 10 2005 "Does" is only relevant for a given compiler If you are talking in gener...
- Ivan Senji (13/45) Dec 10 2005 I think there should be only one syntax tree.
- Chris Sauls (14/43) Dec 10 2005 According to my understanding of the docs, and experience with D, the se...
- Manfred Nowak (23/28) Dec 10 2005 Why is everyone basing her/his tries of proof on non existing
- Sean Kelly (4/12) Dec 10 2005 See my comment about postfix expressions. I would be surprised if D
- Ivan Senji (4/38) Dec 10 2005 But how is a+b+c; simillar to a.b().c();
- Manfred Nowak (16/17) Dec 10 2005 Let [] denote the ordering for type deduction and {}n denote the
- Ivan Senji (20/43) Dec 10 2005 I am sorry but none of the other evaluation orders is possible.
- Manfred Nowak (7/8) Dec 10 2005 [...]
- Ivan Senji (5/17) Dec 10 2005 No. I am just trying to explain why the example you think is illegal is
- Manfred Nowak (5/6) Dec 10 2005 In case of your example: yes, its illegal. But that does not mean,
- Ivan Senji (2/12) Dec 10 2005 Can you please enlighten me and explain why is x.a().b().c() illegal?
- Manfred Nowak (34/36) Dec 10 2005 As stated above `x.a().b().c()' is illegal in the general case.
- BCS (12/28) Dec 10 2005 I think the question is how can x.a().b().c() be evaluated in an order O...
- Manfred Nowak (14/35) Dec 10 2005 Sorry to say that to a really intelligent one: you are wrong. Look
- BCS (17/35) Dec 10 2005 Egg on my face (ick :-p) this can only be compiled one way:
- Manfred Nowak (7/9) Dec 10 2005 Because delegates are bound to their environment, creating the need
- Ivan Senji (22/73) Dec 10 2005 But this probably refers to the parenthesis in expressions, nut to
- Derek Parnell (21/21) Dec 10 2005 On Sat, 10 Dec 2005 11:33:39 +0000 (UTC), Manfred Nowak wrote:
- Manfred Nowak (27/35) Dec 10 2005 [...]
- MWolf (5/34) Dec 10 2005 This is completely ridiculous - it cannot evaluate x.a().b().c() withou...
- Manfred Nowak (9/13) Dec 10 2005 [...]
- Chris Sauls (9/12) Dec 10 2005 The problem with this, is that using typeof() changes the semantics of t...
- Chris Sauls (16/47) Dec 10 2005 I don't claim any such thing. In fact I'm claiming the /opposite/, whic...
- Manfred Nowak (21/24) Dec 10 2005 [...]
- Chris Sauls (12/18) Dec 10 2005 But yet, in a previous post you stated:
- Manfred Nowak (10/16) Dec 10 2005 Right. I had serious concern in writing about values, because I meant
- BCS (18/39) Dec 10 2005 [...]
- Walter Bright (24/28) Dec 13 2005 I've been intending for a while now to revise that to make the order of
- Kris (76/90) Dec 09 2005 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Sean Kelly (13/53) Dec 09 2005 I love the "whisper" syntax. However, it's worth noting that it's a
- Kris (9/20) Dec 09 2005 Mango.io does that by sharing the buffer between reader and writer:
- mclysenk (32/38) Dec 10 2005 While I must concur with Kris and Derek that the whisper syntax is unamb...
- Kris (138/155) Dec 10 2005 ~~~~~~~~~~~~~~~~
- Sean Kelly (22/64) Dec 10 2005 I think it is atomic at the function level, though mostly as a
- Kris (10/34) Dec 10 2005 I've always felt that writable globals were terribly poor-form in thread...
- Ben Hinkle (3/6) Dec 10 2005 I can't see where writef allocates memory - though I haven't looked all ...
- Kris (27/32) Dec 10 2005 Ben; I don't think Sean actually said that writef() did? I can point you...
- Ben Hinkle (13/47) Dec 10 2005 I agree toString typically allocates memory. I don't really follow the
- Kris (9/19) Dec 10 2005 Simply noted that one is often used in place of the other. Both sprintf(...
- Ben Hinkle (8/11) Dec 11 2005 My opinions were expressed in the previous threads.
- Sean Kelly (14/21) Dec 11 2005 I don't think writef does allocate memory, though Object.toString() is
- Kris (5/9) Dec 11 2005 That's a small misconception, since mango.io does have out-of-the-box
- Sean Kelly (3/13) Dec 11 2005 Ah nice. I only read a few of the posts in the last thread :-)
- Kris (3/4) Dec 11 2005 (* splutter *)
I've just seen in one Mango example: Stdout (CR) ("files:") (CR); What should it mean? ??? Stdout(CR,"files:",CR) ??? Thanks, Tamás P.S: Some special possibilities of syntax are not intended for making sources unreadable ... Ex.: "a[--b]|=(c=(25+(--d)))" could be legal in C, but for what? ;-)
Dec 09 2005
"MIcroWizard" <MIcroWizard_member pathlink.com> wrote in message news:dnbut2$17pk$1 digitaldaemon.com...I've just seen in one Mango example: Stdout (CR) ("files:") (CR);I haven't used Mango, but I bet it does this: class SomeOutputStream { SomeOutputStream opCall(whatever) { .... // outputs the whatever to the stream buffer return this; // important } } SomeOutputStream Stdout = new SomeOutputStream(); That way, when opCall is called onStdout, you can chain it. This is because: 1) Stdout(CR) outputs CR (carriage return), then returns itself. 2) Since Stdout(CR) evaluates to Stdout, the next set of parens ("files:") is evaluated as another opCall. That again returns Stdout. 3) Same thing again for the second (CR). So what's really happening is: Stdout(CR); Stdout("files:"); Stdout(CR); It's a clever way of saving space. If you've ever used C++, and used cout: cout << "something" << endl; It's the same thing, but using the << operator instead of the () operator.
Dec 09 2005
Jarrett Billingsley wrote:"MIcroWizard" <MIcroWizard_member pathlink.com> wrote in message news:dnbut2$17pk$1 digitaldaemon.com...In fact that's exactly what it does... This was originally called the "Whisper" notation. It's rather nice, and I like it. What do y'all think? ~John DemmeI've just seen in one Mango example: Stdout (CR) ("files:") (CR);I haven't used Mango, but I bet it does this: class SomeOutputStream { SomeOutputStream opCall(whatever) { .... // outputs the whatever to the stream buffer return this; // important } } SomeOutputStream Stdout = new SomeOutputStream(); That way, when opCall is called onStdout, you can chain it. This is because: 1) Stdout(CR) outputs CR (carriage return), then returns itself. 2) Since Stdout(CR) evaluates to Stdout, the next set of parens ("files:") is evaluated as another opCall. That again returns Stdout. 3) Same thing again for the second (CR). So what's really happening is: Stdout(CR); Stdout("files:"); Stdout(CR); It's a clever way of saving space. If you've ever used C++, and used cout: cout << "something" << endl; It's the same thing, but using the << operator instead of the () operator.
Dec 09 2005
John Demme wrote: [...]This was originally called the "Whisper" notation. It's rather nice, and I like it. What do y'all think?I do not find it that nice, because the whisper notation restricts the power of D to regular expressions over arguments lists, whereas context free languages over arguments lists are possible. I have written on that several month ago and proposed to drop this restriction by using | Stdout (CR; "files:"; CR); instead of | Stdout (CR) ("files:") (CR); Moreover the behaviour of the whisper notation is undefined according to the specs, which clearly state that the order of all expression evaluations are undefined if not explicitely defined---and no definition for the whisper notation is given. Therefore Mango is not portable at present between compilers. To make it portable instead of "Stdout(CR)("files:")(CR);" Mango should contain | ((Stdout(CR))("files:"))(CR); which is probably what is meant. Still I would prefer | Stdout(CR; "files:"; CR); or similar which on my opinion is as well more readable as freeing the full power of D. -manfred
Dec 09 2005
In article <Xns9727D45B325B1svv1999hotmailcom 63.105.9.61>, Manfred Nowak says...John Demme wrote: [...]I'd hate to have you re-explain yourself on this thread, but I simply do not understand your argument. May I ask that you please indulge this poor fool? :) To me, the line: Aliases to nothing other than: I can't see any kind of restriction on the language itself; unless you're talking about the potential for future behaviors? In that case I'd have to agree, but that decision was made a long time before this kind of use for opCall was conceived. - EricAnderton at yahooThis was originally called the "Whisper" notation. It's rather nice, and I like it. What do y'all think?I do not find it that nice, because the whisper notation restricts the power of D to regular expressions over arguments lists, whereas context free languages over arguments lists are possible. I have written on that several month ago and proposed to drop this restriction by using | Stdout (CR; "files:"; CR); instead of | Stdout (CR) ("files:") (CR); Moreover the behaviour of the whisper notation is undefined according to the specs, which clearly state that the order of all expression evaluations are undefined if not explicitely defined---and no definition for the whisper notation is given. Therefore Mango is not portable at present between compilers. To make it portable instead of "Stdout(CR)("files:")(CR);" Mango should contain | ((Stdout(CR))("files:"))(CR); which is probably what is meant. Still I would prefer | Stdout(CR; "files:"; CR); or similar which on my opinion is as well more readable as freeing the full power of D. -manfred
Dec 09 2005
pragma wrote:I'd hate to have you re-explain yourself on this thread, but I simply do not understand your argument. May I ask that you please indulge this poor fool? :) To me, the line: Aliases to nothing other than:Agreed. Where does expression ordering ambiguity come into play here? Sean
Dec 09 2005
On Fri, 9 Dec 2005 20:22:59 +0000 (UTC), pragma wrote:To me, the line: Aliases to nothing other than:As the sequence of evaluation is not defined (in the general case), this might output ... world hello in some implementations of D. -- Derek Parnell Melbourne, Australia 10/12/2005 7:34:02 AM
Dec 09 2005
Derek Parnell wrote: [...]As the sequence of evaluation is not defined (in the general case), this might output ... world hello in some implementations of D.Very true. The pitfall here is that some seem to think, that if an evaluation in one phase is possible it must also be done in one phase. But according to the specs the compiler is not forbidden to use two phases: First phase: determine the functions to be called for every list of arguments. I.e. in the example given: Stdout.opCall(CR), Stdout.opCall("hello"), ... Second phase: execute the functions determined in phase one on their associated arguments lists *in any order* -manfred
Dec 09 2005
"Manfred Nowak" <svv1999 hotmail.com> wroteThe pitfall here is that some seem to think, that if an evaluation in one phase is possible it must also be done in one phase. But according to the specs the compiler is not forbidden to use two phases: First phase: determine the functions to be called for every list of arguments. I.e. in the example given: Stdout.opCall(CR), Stdout.opCall("hello"), ... Second phase: execute the functions determined in phase one on their associated arguments lists *in any order*~~~~~~~~~~~~~~~~~~~~~~~~~~~ There's no doubt that *your* example above could be reordered, but we're not discussing such an example; are we. Said example appears to have confused chaining with comma-seperated expressions. They are quite different animals. Returning to the relevant example: classRef.method1().method2().method3(); Each dereferenced lValue *cannot be determined* until the appropriate call has been made. You see? The expression evaluates deterministically, because of one simple observation: the very act of retrieving each subsequent lValue has the "side effect" of executing each method in the desired order. Again, to unwind: auto a = classRef.method1(); auto b = a.method2(); b.method3(); That is; method2 cannot be called until method1 returns an lValue. Method3 cannot be called until method2 returns an lValue. These are cleary ordered, and this is what chaining effects ... something entirely different from what you appear to be stating. To address your example, these two expressions are not equivalent ~ they're barely even related: 1) classRef.method1().method2().method3(); 2) classRef.method1(), classRef.method2(), classRef.method3(); Does that help?
Dec 09 2005
Kris wrote: [...]To address your example, these two expressions are not equivalent ~ they're barely even related: 1) classRef.method1().method2().method3(); 2) classRef.method1(), classRef.method2(), classRef.method3(); Does that help?No. I see your arguments as beeing totally contradictory. The evaluation order under 1) is undefined per defintition, whereas the order of evaluation under 2) is defined by left to right evaluation and in addition returning the type of the rightmost assignexpression. If you cannot recognize the contradiction to your own argument given in the part cited with [...] it is useless to post any further. -manfred
Dec 09 2005
Manfred Nowak wrote:Kris wrote: [...]I don't think Kris meant to imply that the comma operator was being used in method 2. I think he was using that syntax because it was what you used in the post he replied to. SeanTo address your example, these two expressions are not equivalent ~ they're barely even related: 1) classRef.method1().method2().method3(); 2) classRef.method1(), classRef.method2(), classRef.method3(); Does that help?No. I see your arguments as beeing totally contradictory. The evaluation order under 1) is undefined per defintition, whereas the order of evaluation under 2) is defined by left to right evaluation and in addition returning the type of the rightmost assignexpression. If you cannot recognize the contradiction to your own argument given in the part cited with [...] it is useless to post any further.
Dec 09 2005
Sean Kelly wrote: [..]I don't think Kris meant to imply that the comma operator was being used in method 2. I think he was using that syntax because it was what you used in the post he replied to.Maybe, but he uses the word "expression" together with a `;' at the end of that "expressions". I did not use either and when I want to insert a peace of code into plain text I use "`" and "'" to express that. Agreed, that I forgot on this by writing | Stdout.opCall(CR), Stdout.opCall("hello"), ... instead of `Stdout.opCall(CR)', `Stdout.opCall("hello")', ... which I intended to be an enumeration of a set of calls in natural language. And agreed also, that we will babelize all discussions if we use notations that are ambiguous. -manfred
Dec 09 2005
"Manfred Nowak" <svv1999 hotmail.com>...If you cannot recognize the contradiction to your own argument given in the part cited with [...] it is useless to post any further.OK ... I'm confused. Can someone /please/ explain what this supposed contradiction is about? I'm reposting the [...] snippet referred to above: ========================== classRef.method1().method2().method3(); Each dereferenced lValue *cannot be determined* until the appropriate call has been made. You see? The expression evaluates deterministically, because of one simple observation: the very act of retrieving each subsequent lValue has the "side effect" of executing each method in the desired order. Again, to unwind: auto a = classRef.method1(); auto b = a.method2(); b.method3(); That is; method2 cannot be called until method1 returns an lValue. Method3 cannot be called until method2 returns an lValue. These are cleary ordered, and this is what chaining effects ... something entirely different from what you appear to be stating. ===========================
Dec 09 2005
On Fri, 9 Dec 2005 22:52:16 -0800, Kris wrote:"Manfred Nowak" <svv1999 hotmail.com>...Sorry but I don't know what Manfred is talking about either.If you cannot recognize the contradiction to your own argument given in the part cited with [...] it is useless to post any further.OK ... I'm confused. Can someone /please/ explain what this supposed contradiction is about?I'm reposting the [...] snippet referred to above: ========================== classRef.method1().method2().method3();I'm with you Kris on this too, BTW. If we assume left-to-right scanning the code generation is fairly obvious. But even if the compiler scans right-to-left, I see it goes something like this ... It finds .method3() This is a function call of a member of some class, struct or module. So what is it? It then finds .method2() This is also a call to a member of something, so I have to find out what so I can work out what it's return value is so I can then find out if method3() is a valid member of whatever method2() returns. It then finds .method1() Ditto. It then finds classRef So now it can tell what method1() returns, and thus if method2() is valid, and so forth for method3(). And this is all before generating any code. So now it can generate code to correctly invoke method3(). It must generate code to call method1() first, then using its return value to call method2() then its return to call method3(). Thus this syntax will always work as a chained call. -- Derek Parnell Melbourne, Australia 10/12/2005 7:17:19 PM
Dec 10 2005
Thank you, Derek. "Derek Parnell" <derek psych.ward> wrote in message news:1d9fnb3xux6cb$.eeeyn889n1f6$.dlg 40tude.net...On Fri, 9 Dec 2005 22:52:16 -0800, Kris wrote:"Manfred Nowak" <svv1999 hotmail.com>...Sorry but I don't know what Manfred is talking about either.If you cannot recognize the contradiction to your own argument given in the part cited with [...] it is useless to post any further.OK ... I'm confused. Can someone /please/ explain what this supposed contradiction is about?I'm reposting the [...] snippet referred to above: ========================== classRef.method1().method2().method3();I'm with you Kris on this too, BTW. If we assume left-to-right scanning the code generation is fairly obvious. But even if the compiler scans right-to-left, I see it goes something like this ... It finds .method3() This is a function call of a member of some class, struct or module. So what is it? It then finds .method2() This is also a call to a member of something, so I have to find out what so I can work out what it's return value is so I can then find out if method3() is a valid member of whatever method2() returns. It then finds .method1() Ditto. It then finds classRef So now it can tell what method1() returns, and thus if method2() is valid, and so forth for method3(). And this is all before generating any code. So now it can generate code to correctly invoke method3(). It must generate code to call method1() first, then using its return value to call method2() then its return to call method3(). Thus this syntax will always work as a chained call. -- Derek Parnell Melbourne, Australia 10/12/2005 7:17:19 PM
Dec 10 2005
"Manfred Nowak" <svv1999 hotmail.com> wroteMoreover 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.That's an interesting point. I'll counter it by noting that the compiler has little choice about the order of evaluation here: classRef.method1().method2().method3(); It clearly cannot evaluate right to left, so it must start at the lhs. An implementation might then execute each method in random order, but what's the point? Further, method1() may return a reference to a different class reference, which means that it must be evaluated before method2() can be invoked. And so on. It's akin to unwrapping the expression like so: auto a = classRef.method1(); auto b = a.method2(); b.method3(); It may be that D documentation does not explicitly state this behaviour, but it perhaps should? Of course, DMD does a fine job optimizing such back-to-back calls. I understand GDC does a fine job also.| Stdout(CR; "files:"; CR);This is a perfectly valid approach also. Mango.io was around long before D gained typeinfo for variadic arguments, so the option wasn't available then. Mango.io does use the variadic approach for printf() style formatting, so both are present for comparative purposes. On reflection, mango.io would still have chosen "whisper" for general purposes, since it's more flexible. - Kris "Manfred Nowak" <svv1999 hotmail.com> wrote in message news:Xns9727D45B325B1svv1999hotmailcom 63.105.9.61...John Demme wrote: [...]This was originally called the "Whisper" notation. It's rather nice, and I like it. What do y'all think?I do not find it that nice, because the whisper notation restricts the power of D to regular expressions over arguments lists, whereas context free languages over arguments lists are possible. I have written on that several month ago and proposed to drop this restriction by using | Stdout (CR; "files:"; CR); instead of | Stdout (CR) ("files:") (CR); Moreover the behaviour of the whisper notation is undefined according to the specs, which clearly state that the order of all expression evaluations are undefined if not explicitely defined---and no definition for the whisper notation is given. Therefore Mango is not portable at present between compilers. To make it portable instead of "Stdout(CR)("files:")(CR);" Mango should contain | ((Stdout(CR))("files:"))(CR); which is probably what is meant. Still I would prefer | Stdout(CR; "files:"; CR); or similar which on my opinion is as well more readable as freeing the full power of D. -manfred
Dec 09 2005
On Fri, 9 Dec 2005 12:52:54 -0800, Kris wrote:It may be that D documentation does not explicitly state this behaviour, but it perhaps should?Yes, that would clear things up nicely. I guess that a statement such as b + c + d; could evaluate to b.opAdd(c).opAdd(d) or d.opAdd(c).opAdd(b) but once the evaluation was set, the execution would proceed in left to right order. -- Derek Parnell Melbourne, Australia 10/12/2005 7:57:43 AM
Dec 09 2005
In article <gf10ceogj4nu.1k0swchr1kpfj.dlg 40tude.net>, Derek Parnell says...On Fri, 9 Dec 2005 12:52:54 -0800, Kris wrote:the order of evaluation can make a difference even without side effects Example (contrived): class A { B opAdd(C); A opAdd(A) }; class C { C opAdd(C); A opAdd(B) }; class B { }; A a; B b; C c; auto a2 = a + (b + c); // a2 is A auto c2 = (a + b) + c; // c2 is C auto q = a + b + c; // q is A or C??It may be that D documentation does not explicitly state this behaviour, but it perhaps should?Yes, that would clear things up nicely. I guess that a statement such as b + c + d; could evaluate to b.opAdd(c).opAdd(d) or d.opAdd(c).opAdd(b) but once the evaluation was set, the execution would proceed in left to right order. -- Derek Parnell Melbourne, Australia 10/12/2005 7:57:43 AM
Dec 09 2005
BCS wrote: [...]auto q = a + b + c; // q is A or C??very nice example :-) ... and this is also a severe blow onto the secureness of the whisper notation. -manfred
Dec 09 2005
"Manfred Nowak"... and this is also a severe blow onto the secureness of the whisper notation.[cue music from "Rocky", or "Jaws"] The drama! :-D DMD for both Win32 & linux supports method chaining. GDC supports method chaining. I believe Walter intends D to support method chaining. You don't see anything about it in the spec ... Instead of the "severe blow" dramatics, why don't you simply ask Walter if method-chaining is supported or not? If it is an illegal or non-deterministic construct, then I'll change the way Mango.io operates ~ no big deal. If it is officially supported, then you can derive some pleasure giving Walter hell for not writing about it :) - Kris
Dec 09 2005
"BCS" <BCS_member pathlink.com> wrote in message news:dnctp1$k9k$1 digitaldaemon.com...In article <gf10ceogj4nu.1k0swchr1kpfj.dlg 40tude.net>, Derek Parnellsays...Operator precedence requires this evaluation.I guess that a statement such as b + c + d; could evaluate to b.opAdd(c).opAdd(d)No.or d.opAdd(c).opAdd(b)No, *that* is currently undefined. It is currently undefined whether b, c, or d is evaluated first. The operator precedence, however, *is* nailed down.but once the evaluation was set, the execution would proceed in left to right order.the order of evaluation can make a difference even without side effects Example (contrived): class A { B opAdd(C); A opAdd(A) }; class C { C opAdd(C); A opAdd(B) }; class B { }; A a; B b; C c; auto a2 = a + (b + c); // a2 is ACorrect.auto c2 = (a + b) + c; // c2 is CThis will fail, as there is no match for (a + b).auto q = a + b + c; // q is A or C??It will fail, because it parses as (a+b)+c which has no match for (a+b).
Dec 13 2005
Walter Bright wrote:"BCS" <BCS_member pathlink.com> wrote in message news:dnctp1$k9k$1 digitaldaemon.com...oops, my bad, it was supposed to be: class A { A opAdd(A); C opAdd(B); B opAdd(C); }; class B { C opAdd(A); B opAdd(B); A opAdd(C); }; class C { B opAdd(A); A opAdd(B); C opAdd(C); }; (I got lazy and didn't want to type all of them out) As to a+b+c being unambiguous, I can't find the rules that make that true. Could you please point me to them?In article <gf10ceogj4nu.1k0swchr1kpfj.dlg 40tude.net>, Derek Parnellsays...Operator precedence requires this evaluation.I guess that a statement such as b + c + d; could evaluate to b.opAdd(c).opAdd(d)No.or d.opAdd(c).opAdd(b)No, *that* is currently undefined. It is currently undefined whether b, c, or d is evaluated first. The operator precedence, however, *is* nailed down.but once the evaluation was set, the execution would proceed in left to right order.the order of evaluation can make a difference even without side effects Example (contrived): class A { B opAdd(C); A opAdd(A) }; class C { C opAdd(C); A opAdd(B) }; class B { }; A a; B b; C c; auto a2 = a + (b + c); // a2 is ACorrect.auto c2 = (a + b) + c; // c2 is CThis will fail, as there is no match for (a + b).auto q = a + b + c; // q is A or C??It will fail, because it parses as (a+b)+c which has no match for (a+b).
Dec 13 2005
"BCS" <BCS_member pathlink.com> wrote in message news:dnn794$1psk$1 digitaldaemon.com...As to a+b+c being unambiguous, I can't find the rules that make that true. Could you please point me to them?It's implicit in the grammar for AddExpression's.
Dec 13 2005
Walter Bright wrote:"BCS" <BCS_member pathlink.com> wrote in message news:dnn794$1psk$1 digitaldaemon.com...I just reread that, and I don't see it. (I'm looking at http://www.digitalmars.com/d/expression.html#AddExpression , is this the right place )As to a+b+c being unambiguous, I can't find the rules that make that true. Could you please point me to them?It's implicit in the grammar for AddExpression's.
Dec 13 2005
BCS wrote:Walter Bright wrote:First, you should trust Walter when he says something :) And to the point - AddExpr is defined as (among others) AddExpr + MulExpr So, if you have a+b+c, it can't parse as a+(b+c), as that would mean that "a" is an AddExpr and "b+c" must then be a MulExpr, which it obviously can't be. OTOH, (a+b)+c does fit the grammar.. xs0"BCS" <BCS_member pathlink.com> wrote in message news:dnn794$1psk$1 digitaldaemon.com...I just reread that, and I don't see it. (I'm looking at http://www.digitalmars.com/d/expression.html#AddExpression , is this the right place )As to a+b+c being unambiguous, I can't find the rules that make that true. Could you please point me to them?It's implicit in the grammar for AddExpression's.
Dec 13 2005
xs0 wrote:BCS wrote:Thank you for that explanation, (I was looking in the text so I missed that). Also, I was trying to ask the question as "You say its true so I must be a dumb schmuck for not finding it." -BCS "I would rather feel like an fool now for asking a dumb question then tomorrow for not asking a smart one ."Walter Bright wrote:First, you should trust Walter when he says something :) And to the point - AddExpr is defined as (among others) AddExpr + MulExpr So, if you have a+b+c, it can't parse as a+(b+c), as that would mean that "a" is an AddExpr and "b+c" must then be a MulExpr, which it obviously can't be. OTOH, (a+b)+c does fit the grammar.. xs0"BCS" <BCS_member pathlink.com> wrote in message news:dnn794$1psk$1 digitaldaemon.com...I just reread that, and I don't see it. (I'm looking at http://www.digitalmars.com/d/expression.html#AddExpression , is this the right place )As to a+b+c being unambiguous, I can't find the rules that make that true. Could you please point me to them?It's implicit in the grammar for AddExpression's.
Dec 13 2005
BCS wrote:Walter Bright wrote:I believe this is because expressions are evaluated left to right, assuming equal operator precedence. This seems to be a standard rule for expression evaluation regardless of the programming language. What is not predetermined (as Walter points out) is the evaluation of each member of an expression, as this should not affect the evaluated result. Typically, building side-effects into the evaluation of expression members is considered bad form, and except for the few defined cases such as short-circuit evaluation, the order in which these members are evaluated is undefined. Sean"BCS" <BCS_member pathlink.com> wrote in message news:dnn794$1psk$1 digitaldaemon.com...I just reread that, and I don't see it.As to a+b+c being unambiguous, I can't find the rules that make that true. Could you please point me to them?It's implicit in the grammar for AddExpression's.
Dec 13 2005
Derek Parnell wrote:On Fri, 9 Dec 2005 12:52:54 -0800, Kris wrote:Doubtful. It is not clear in the spec, but I bet in D the operator evaluation order is well-defined, and is the same as C/C++. Thus "b + c + d" evaluates to: ((b + c) + d) and then to the corresponding opAdd's. And the function call operator sequences evaluate left to right too. What is not well defined, both in C/C++ and in D are the expression *components* order of evaluation. Stuff like (straight from the docs): i = ++i; c = a + (a = b); func(++i, ++i); which is an ambiguity that you *can not even* resolve with parenthesis. -- Bruno Medeiros - CS/E student "Certain aspects of D are a pathway to many abilities some consider to be... unnatural."It may be that D documentation does not explicitly state this behaviour, but it perhaps should?Yes, that would clear things up nicely. I guess that a statement such as b + c + d; could evaluate to b.opAdd(c).opAdd(d) or d.opAdd(c).opAdd(b)
Dec 09 2005
Bruno Medeiros wrote:Derek Parnell wrote:Exactly. In fact, most of this behavior is undefined in C++ for this very reason. For example, it is illegal to modify a scalar value more than once in an expression. One such example of undefined behavior in C++ is this: i = a[++i]; SeanOn Fri, 9 Dec 2005 12:52:54 -0800, Kris wrote:Doubtful. It is not clear in the spec, but I bet in D the operator evaluation order is well-defined, and is the same as C/C++. Thus "b + c + d" evaluates to: ((b + c) + d) and then to the corresponding opAdd's. And the function call operator sequences evaluate left to right too. What is not well defined, both in C/C++ and in D are the expression *components* order of evaluation. Stuff like (straight from the docs): i = ++i; c = a + (a = b); func(++i, ++i); which is an ambiguity that you *can not even* resolve with parenthesis.It may be that D documentation does not explicitly state this behaviour, but it perhaps should?Yes, that would clear things up nicely. I guess that a statement such as b + c + d; could evaluate to b.opAdd(c).opAdd(d) or d.opAdd(c).opAdd(b)
Dec 09 2005
Bruno Medeiros wrote:Doubtful. It is not clear in the spec, but I bet in D the operator evaluation order is well-defined, and is the same as C/C++.In fact, check the D expressions spec( http://www.digitalmars.com/d/expression.html ) and a C/C++ operator precedence table ( http://www.difranco.net/cop2220/op-prec.htm ) . You can note that the D grammar has the operators occuring in the exact inverse order as the precedence table, i.e., the grammar is built to support the same precedence semantics as C/C++ . Walter should state this explicitly on the spec, though. -- Bruno Medeiros - CS/E student "Certain aspects of D are a pathway to many abilities some consider to be... unnatural."
Dec 09 2005
Bruno Medeiros wrote: [...]the grammar is built to support the same precedence semantics as C/C++ .[...] Then what? What has precedence to do with evaluation order and associativeness? But this deviations should be entered into the "C to D" and "C++ to D" guides. -manfred
Dec 09 2005
Manfred Nowak wrote:Bruno Medeiros wrote: [...]I was thinking "precedence" in the general sense, that is both precedence between different operators (precedence per se) and precedence between several instances of the same operator (associativity). In other words: "the grammar is built to support the same precedence and associativity semantics as C/C++" (I think this was somewhat implicitly obvious, because why would Walter want to copy only part of the C/C++ operator evalution rules...) -- Bruno Medeiros - CS/E student "Certain aspects of D are a pathway to many abilities some consider to be... unnatural."the grammar is built to support the same precedence semantics as C/C++ .[...] Then what? What has precedence to do with evaluation order and associativeness?
Dec 10 2005
Bruno Medeiros wrote: [...]In other words: "the grammar is built to support the same precedence and associativity semantics as C/C++"Look at the promise of not reordering (sub)expressions for the operators `+' and `*' in case one of their operands is a floating point value. What a nuisance if reordering genrally does not take place. -manfred
Dec 10 2005
Kris wrote:"Manfred Nowak" <svv1999 hotmail.com> wroteI think it should. The C++ spec states that "postfix expressions group left to right" and opCall is clearly a postfix expression. Also, it seems to be a primary design goal of D to preserve C/C++ expression syntax rules whenever possible as not doing so would be confusing and would lead to subtle bugs when porting code. SeanMoreover 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.That's an interesting point. I'll counter it by noting that the compiler has little choice about the order of evaluation here: classRef.method1().method2().method3(); It clearly cannot evaluate right to left, so it must start at the lhs. An implementation might then execute each method in random order, but what's the point? Further, method1() may return a reference to a different class reference, which means that it must be evaluated before method2() can be invoked. And so on. It's akin to unwrapping the expression like so: auto a = classRef.method1(); auto b = a.method2(); b.method3(); It may be that D documentation does not explicitly state this behaviour, but it perhaps should? Of course, DMD does a fine job optimizing such back-to-back calls. I understand GDC does a fine job also.
Dec 09 2005
Kris wrote: [...]An implementation might then execute each method in random order, but what's the point?[...] Other way round: what's the point to not define the order? Because the compiler might see optimizations and adaptions to the underlying hardware which now everyone is unable to imagine. Future is always unpredictable ;-) Therefore Walters decision to not define the order is correct in the general case. If you want it defined split the expression by using parentheses or temporal variables. If in the latter case one wants syntactical sugar one should state it as such. -manfred
Dec 09 2005
Manfred Nowak wrote:Kris wrote: [...]If the order is not defined, then each implementation would be able to choose a different order, which would yield different program results. That is no optimization, and such ambiguous code would be useless.An implementation might then execute each method in random order, but what's the point?[...] Other way round: what's the point to not define the order? Because the compiler might see optimizations and adaptions to the underlying hardware which now everyone is unable to imagine. Future is always unpredictable ;-)Therefore Walters decision to not define the order is correct in the general case. If you want it defined split the expression by using parentheses or temporal variables.You are under some wrong assumptions. See my post replying to Derek. -- Bruno Medeiros - CS/E student "Certain aspects of D are a pathway to many abilities some consider to be... unnatural."
Dec 09 2005
In article <dnd99m$1j9d$1 digitaldaemon.com>, Bruno Medeiros says... [...]If the order is not defined, then each implementation would be able to choose a different order, which would yield different program results. That is no optimization, and such ambiguous code would be useless.Ambiguous code, as described above, is illegal code. If order of evaluation makes a difference, that the compiler can flag it as an error. from docs: "Unless otherwise specified, the implementation is free to evaluate the components of an expression in any order. It is an error to depend on order of evaluation when it is not specified. [...] If the compiler can determine that the result of an expression is illegally dependent on the order of evaluation, it can issue an error (but is not required to). The ability to detect these kinds of errors is a quality of implementation issue." as to the evaluation of the "wisper syntax" how about the following struct A { int opCall(int); } struct B { B opCall(A); B opCall(int){return this;} } A a; B b; int i; // is this b.(a)(i); //this int t1 = a.opCall(i) b.opCall(t1); //or this B t2 = b.opCall(a); t2.opCall(i);
Dec 09 2005
BCS wrote:In article <dnd99m$1j9d$1 digitaldaemon.com>, Bruno Medeiros says... [...]That's complete nonsense. I don't see what you're getting at. `b.(a)' is not a valid expression according to the docs (http://digitalmars.com/d/expression.html). A dot operator must be followed by an /Identifier/ in all the expression rules stated on that page. If these rules were written such that an /Expression/ would follow the dot operator, then your case would make *some* sense. If you have any argument, then please point me to the docs where it says this expression is legal.If the order is not defined, then each implementation would be able to choose a different order, which would yield different program results. That is no optimization, and such ambiguous code would be useless.Ambiguous code, as described above, is illegal code. If order of evaluation makes a difference, that the compiler can flag it as an error. from docs: "Unless otherwise specified, the implementation is free to evaluate the components of an expression in any order. It is an error to depend on order of evaluation when it is not specified. [...] If the compiler can determine that the result of an expression is illegally dependent on the order of evaluation, it can issue an error (but is not required to). The ability to detect these kinds of errors is a quality of implementation issue." as to the evaluation of the "wisper syntax" how about the following struct A { int opCall(int); } struct B { B opCall(A); B opCall(int){return this;} } A a; B b; int i; // is this b.(a)(i); //this int t1 = a.opCall(i) b.opCall(t1); //or this B t2 = b.opCall(a); t2.opCall(i);
Dec 09 2005
James Dunne wrote: [...]If you have any argument, then please point me to the docs where it says this expression is legal.You are right in that there is a typo. Simply suppose that it should be read `b(a)(i);' -manfred
Dec 09 2005
BCS wrote: [...]as to the evaluation of the "wisper syntax" how about the following[...] Wohoo! Second strike. You are really clever! Your idea reminds me on how the macro processor "m4" evaluates the arguments given to its macros. -manfred
Dec 09 2005
"Manfred Nowak" <svv1999 hotmail.com> wrote...BCS wrote: [...]And you, my friend, are quite foolish, trolling, or both.as to the evaluation of the "wisper syntax" how about the following[...] Wohoo! Second strike. You are really clever!
Dec 10 2005
Kris wrote: [something] People living in cages always argue that free people are foolish, because the practice of living in a cage shows that after going more than five steps in one direction you hit the wall. -manfred
Dec 10 2005
(*cough*) ROFL! I really thought you had no sense of humour, manfred. But it's clear you're a total comedienne :-D BTW: you look foolish partly because of your blatant and outright gloating over something that (a) didn't hold any water to begin with and (b) was subsequently retracted as such. You went out on a limb in order to pour scorn and detriment upon others, and then promptly fell out of the tree. If you hadn't done that, you might have saved yourself some grief. Thus, you really should try to take some responsibility and some humility upon yourself; heck, you *could* even note that "perhaps you made a mistake"? That might restore a bit of faith in your intent? We all make mistakes; I make 'em all the time. This post is probably another one. Good luck! "Manfred Nowak" <svv1999 hotmail.com> wrote in message news:Xns9729381587AC6svv1999hotmailcom 63.105.9.61...Kris wrote: [something] People living in cages always argue that free people are foolish, because the practice of living in a cage shows that after going more than five steps in one direction you hit the wall. -manfred
Dec 10 2005
On Sun, 11 Dec 2005 04:30:47 +0000 (UTC), Manfred Nowak wrote:Kris wrote: [something] People living in cages always argue that free people are foolish, because the practice of living in a cage shows that after going more than five steps in one direction you hit the wall. -manfredI assume by this that you hit a wall, Manfred. No worries; doesn't matter. We all make mistakes and its good for our souls when we admit it publicly. You had me going there for moment; I was almost about to add you to my troll-file. What does irk me though is that most of this pointless discussion could have been avoided just by Walter clearly stating his intention for D in this matter. -- Derek Parnell Melbourne, Australia 11/12/2005 4:50:33 PM
Dec 10 2005
"BCS" <BCS_member pathlink.com> wrote [snip]struct A { int opCall(int); } struct B { B opCall(A); B opCall(int){return this;} }A a; B b; // b.(a)(i); b (a) (i); // is this correct? (no dot) For the moment, let's assume that's what you meant? b (a) (i);//this int t1 = a.opCall(i) b.opCall(t1);To get t1, where did the A lvalue come from, for a.opCall(i)? It is conceptually possible to see "(a)" as the A lvalue, with superfluous parens around it. Is this what you are getting at? But then, "(i)" would be treated the same ~ there would be no opCall at all. At that point there would be a syntax error with an orphaned "b". For opCall to function, I imagine the '(' is being eaten before the parser reaches a 'terminal' state, where the '(' would be considered to be something else, like the start of a subexpression. Of course, one can enforce right-to-left evaluation: b (a (i)); // or b ((a) (i)); Does this answer your question, or were you getting at something else?
Dec 09 2005
Kris wrote: [...]Does this answer your question, or were you getting at something else?Yes. Let's assume the compiler scans the code from left to right, which the compiler isn't enforced to do. Let's further assume, that the code read is `b(a)'. Now your argument is, that the compiler should evaluate `b(a)'. Your further and wrong implicite argument is, that the compiler for this evaluation is restricted to the code portion currently read. Only under this assumption, the compiler has no choice. If this restriction does not exist---and the docs do not say anything about the existence of such restriction---after evaluating the argument list `a' the compiler is not disallowed to scan further, recognizing that a `(' follows, concluding the need to evaluate the `a' to the address of a function, which is provided by the `opCall' in `A', and then bind the `(i)' to that `opCall', resulting in first executing the `a.opCall(i)'. On the other hand: are you able to proof under the restrictions of the current specs that there is only one syntax tree possible, when scanning the code from right to left under an LARL-parser? -manfred
Dec 09 2005
"Manfred Nowak" <svv1999 hotmail.com>Kris wrote: [...]Ah. Well, D doesn't operate like that. If it did, that example would result in 3 operands with zero operators. Not much use to anyone? Just like this statement: 3 arf 'c';Does this answer your question, or were you getting at something else?Yes.Let's assume the compiler scans the code from left to right, which the compiler isn't enforced to do. Let's further assume, that the code read is `b(a)'. Now your argument is, that the compiler should evaluate `b(a)'.Not "should". The compiler *does* evaluate `b(a)', and then each subsequent opCall in turn :-)Your further and wrong implicite argument is, that the compiler for this evaluation is restricted to the code portion currently read. Only under this assumption, the compiler has no choice.Patently false ~ you're asserting things that weren't even implied :-)If this restriction does not exist---and the docs do not say anything about the existence of such restriction---after evaluating the argument list `a' the compiler is not disallowed to scan further, recognizing that a `(' follows, concluding the need to evaluate the `a' to the address of a function, which is provided by the `opCall' in `A', and then bind the `(i)' to that `opCall', resulting in first executing the `a.opCall(i)'.Stack fault :: parser failed. Core dumped. Sorry.On the other hand: are you able to proof under the restrictions of the current specs that there is only one syntax tree possible, when scanning the code from right to left under an LARL-parser?Have absolutely zero intention of bothering :-) I'm tired. Don't know about you, but I'm talking about a language claiming to follow on from C++ (and Java for that matter). Both those languages support method-chaining. C supports function chaining. DMD currently supports method chaining on two platforms. GDC supports method chaining. That's three D compilers. Are you arguing that they don't really? Or that they shouldn't? Are you talking about the same language? You've so far stated, in a variety of ways, that the approach chosen by mango.io is broken, non-portable, and somehow restrictive upon the language (eh?). Yet that's clearly not the case ~ mango.io has been working on a number of OS using a number of D compilers. Is your position perhaps about a personal distaste for method-chaining, and a gripe about lack of documentation? Of course, I am making an implicit assumption that you're not trolling. I mean, "Wohoo! Second strike" ?? What is that about? Sounds like a rush of gleeful pettiness? Thus, you've picked entirely the wrong person to have an "academic debate" with. If you want to complain about the documentation not being explicit about chaining behaviour, then take it up with Walter? If he stands up and says "Thou Shalt Not Method-Chain!" , then I will consider the ramifications, and not before. Until then you're simply speculating, blowing lots of vapid air, and we're both wasting bandwidth. You really should ask him ... if you feel you can't do that, then I will. Cheers :-)
Dec 10 2005
Kris wrote: [...]You really should ask him ... if you feel you can't do that, then I will.As a member of this community created by the free will of intelligent people, I have no reason to ask Walter, because I assume that the contents of the current specs with respect to the subject of this thread is intended by Walter and I agree with the conclusions. As a coeditor of the news I will bring anything to his special attention, if someone tells me to do so. -manfred
Dec 10 2005
In article <dne4og$22l$1 digitaldaemon.com>, Kris says... [...]Not "should". The compiler *does* evaluate `b(a)', and then each subsequent opCall in turn :-)"Does" is only relevant for a given compiler If you are talking in general, what is of interest is what the compiler "must" do. [...]Don't know about you, but I'm talking about a language claiming to follow on from C++ (and Java for that matter). Both those languages support method-chaining. C supports function chaining. DMD currently supports method chaining on two platforms. GDC supports method chaining. That's three D compilers.[...] IIRC the front end for all three is identical.Thus, you've picked entirely the wrong person to have an "academic debate" with. If you want to complain about the documentation not being explicit about chaining behaviour, then take it up with Walter?Bingo. This is a question about ambiguities (or lack of) in the language definitions, not current implementations.You really should ask him ... if you feel you can't do that, then I will.Strait to the source: Walter, what is your opinion on this?
Dec 10 2005
Manfred Nowak wrote:Kris wrote: [...]I think there should be only one syntax tree. I think that when people talk about order of evaluation in for example: a.method1().method2(); the question isn't wich one of these methods will be called but with for example: int getI(){static int i = 0; i+=5; return i;} a.method1(getI()).method2(getI()); OK, method1 *must* be called first and then method2 but is this a.method1(5).method2(10); or a.method1(10).method2(5); ?Does this answer your question, or were you getting at something else?Yes. Let's assume the compiler scans the code from left to right, which the compiler isn't enforced to do. Let's further assume, that the code read is `b(a)'. Now your argument is, that the compiler should evaluate `b(a)'. Your further and wrong implicite argument is, that the compiler for this evaluation is restricted to the code portion currently read. Only under this assumption, the compiler has no choice. If this restriction does not exist---and the docs do not say anything about the existence of such restriction---after evaluating the argument list `a' the compiler is not disallowed to scan further, recognizing that a `(' follows, concluding the need to evaluate the `a' to the address of a function, which is provided by the `opCall' in `A', and then bind the `(i)' to that `opCall', resulting in first executing the `a.opCall(i)'. On the other hand: are you able to proof under the restrictions of the current specs that there is only one syntax tree possible, when scanning the code from right to left under an LARL-parser?
Dec 10 2005
BCS wrote:In article <dnd99m$1j9d$1 digitaldaemon.com>, Bruno Medeiros says... as to the evaluation of the "wisper syntax" how about the following struct A { int opCall(int); } struct B { B opCall(A); B opCall(int){return this;} } A a; B b; int i; // is this b(a)(i); //this int t1 = a.opCall(i) b.opCall(t1); //or this B t2 = b.opCall(a); t2.opCall(i);According to my understanding of the docs, and experience with D, the second one. Really this whole discussion is silly. The chained calls in the Whisper syntax can not be reordered, because each subsequent call is dependant on the previous call's return value. In 100% of cases of: It is guaranteed that `?.opCall(b)` can not possibly be executed before `obj.opCall(a)` because of that little question-mark there, which stands for the return value of `obj.opCall(a)`. Its all really very straightforward... in fact it reminds me of the sort of thing that would've been on a test in my High School C++ class. Any expression, B, containing any operand whose value is taken from the result of another expression, A, must be evaluated /after/ the expression, A. QED. Any compiler that doesn't do this... really isn't useful at all. -- Chris Sauls
Dec 10 2005
Chris Sauls wrote: [...]Any expression, B, containing any operand whose value is taken from the result of another expression, A, must be evaluated /after/ the expression, A. QED.Why is everyone basing her/his tries of proof on non existing restrictions? Here you claim, that the values of expressions must be known at compile time, which is clearly wrong. The compiler needs only knowledge upon the types of the expressions to generate code for the values given at runtime. This is true for every subexpression also. Once the types are known the compiler is free to evaluate all parts of the expression in any order, even at random If there is an ambiguity in the type a subexpression can have, the compiler is not forced to detect that ambiguity and report on it. The compiler is allowed to choose out of the ambiguous candidates at random. That means that in the example given by BCS the code fragnment A a2= a+b+c; C c2= a+b+c; may or may not result in reporting compilation errors, which in addition may change between consecutive runs.Any compiler that doesn't do this... really isn't useful at all.For what are lists of statements useful then? Do you claim, that D doesn't need the `;' to string statements? -manfred
Dec 10 2005
Manfred Nowak wrote:Chris Sauls wrote: [...]See my comment about postfix expressions. I would be surprised if D were different than C++ insofar as this is concerned. SeanAny expression, B, containing any operand whose value is taken from the result of another expression, A, must be evaluated /after/ the expression, A. QED.Why is everyone basing her/his tries of proof on non existing restrictions?
Dec 10 2005
Manfred Nowak wrote:Chris Sauls wrote: [...]But how is a+b+c; simillar to a.b().c(); In no way. I agree that a+b+c can be (a+b)+c or a+(b+c) but a.b().c() can only be (a.b()).c() and nothing else.Any expression, B, containing any operand whose value is taken from the result of another expression, A, must be evaluated /after/ the expression, A. QED.Why is everyone basing her/his tries of proof on non existing restrictions? Here you claim, that the values of expressions must be known at compile time, which is clearly wrong. The compiler needs only knowledge upon the types of the expressions to generate code for the values given at runtime. This is true for every subexpression also. Once the types are known the compiler is free to evaluate all parts of the expression in any order, even at random If there is an ambiguity in the type a subexpression can have, the compiler is not forced to detect that ambiguity and report on it. The compiler is allowed to choose out of the ambiguous candidates at random. That means that in the example given by BCS the code fragnment A a2= a+b+c; C c2= a+b+c; may or may not result in reporting compilation errors, which in addition may change between consecutive runs.
Dec 10 2005
Ivan Senji wrote: [...]but a.b().c() can only be (a.b()).c() and nothing else.Let [] denote the ordering for type deduction and {}n denote the ordering for evaluation, where {}i is evaluated before {}j if and only if i<j. You are right, that according to type deduction `x.a().b().c()' can only be annotated by [[x.a()].b()].c() . But according to evaluation ordering still several versions are possible x.{a()}1.{b()}2.{c()}3 // which seems to be the preferred one x.{a()}1.{b()}3.{c()}2 x.{a()}2.{b()}1.{c()}3 x.{a()}2.{b()}3.{c()}1 x.{a()}3.{b()}1.{c()}2 x.{a()}3.{b()}2.{c()}1 -manfred
Dec 10 2005
Manfred Nowak wrote:Ivan Senji wrote: [...]I am sorry but none of the other evaluation orders is possible. For example x is of type X, a returns object of type A, b of type B and c of type C. so we have methods: class X{ A a(); } class A{ B b(); } class B{ C c(); } or we could think of them this way: A a(X x_this); B b(A a_this); C c(C c_this); then the following is also true x.a() === a(x); x.a().b() === b(a(x)); x.a().b().c() === c(b(a(x))); And there is no other possible and meaningful and reasonable order of evaluation then the above. a(x) is evaluated first, then b is called with a's result as an argument, then c is called with b's result as argument.but a.b().c() can only be (a.b()).c() and nothing else.Let [] denote the ordering for type deduction and {}n denote the ordering for evaluation, where {}i is evaluated before {}j if and only if i<j. You are right, that according to type deduction `x.a().b().c()' can only be annotated by [[x.a()].b()].c() . But according to evaluation ordering still several versions are possible x.{a()}1.{b()}2.{c()}3 // which seems to be the preferred one x.{a()}1.{b()}3.{c()}2 x.{a()}2.{b()}1.{c()}3 x.{a()}2.{b()}3.{c()}1 x.{a()}3.{b()}1.{c()}2 x.{a()}3.{b()}2.{c()}1
Dec 10 2005
Ivan Senji wrote: [...]x.a().b().c() === c(b(a(x)));[...] Am I right, that you start to proof your claim that some of the currently illegal expressions should be made legal by an example that is in fact illegal and then stating that it should be legal? -manfred
Dec 10 2005
Manfred Nowak wrote:Ivan Senji wrote: [...]No. I am just trying to explain why the example you think is illegal is in fact legal. So (to make sure) you are saying that x.a().b().c() is illegal? Well if that is so, i think my proof is a good one :)x.a().b().c() === c(b(a(x)));[...] Am I right, that you start to proof your claim that some of the currently illegal expressions should be made legal by an example that is in fact illegal and then stating that it should be legal?
Dec 10 2005
Ivan Senji wrote: [...]So (to make sure) you are saying that x.a().b().c() is illegal?In case of your example: yes, its illegal. But that does not mean, that every string of postfixes is illegal. -manfred
Dec 10 2005
Manfred Nowak wrote:Ivan Senji wrote: [...]Can you please enlighten me and explain why is x.a().b().c() illegal?So (to make sure) you are saying that x.a().b().c() is illegal?In case of your example: yes, its illegal. But that does not mean, that every string of postfixes is illegal.
Dec 10 2005
Ivan Senji wrote: [...]Can you please enlighten me and explain why is x.a().b().c() illegal?As stated above `x.a().b().c()' is illegal in the general case. This stems from the definition in the specs: | Unless otherwise specified, the implementation is free to | evaluate the components of an expression in any order. It is an | error to depend on order of evaluation when it is not | specified. Intensified by | Parenthesis control operator precedence, parenthesis do not | control order of evaluation. In `x.a().b().c()' postfix expressions are stringed together for which the order of evaluation is not specified. Therefore its an error to depend on the order of evaluation. I.e. `x.a().b().c()' is legal only if there is a proof that the order of evaluation does not change the intended result. In case of your example you state, that `x.a().b().c()' is equivalent to `c(b(a(x)))'. This seems to indicate, that `a(x)' must be evaluated first. If so, `x.a().b().c()' is illegal according to the specs. If `a(x)' is not required to be evaluated first every try of proof of independeny of evaluation must fail, because nothing is known of the intended result or the impact of the calls on the intended result. Examples: 1) if `a()', `b()' and `c()' are known to be neutral operations with respect to the intended result, then `x.a().b().c()' is legal 2) if in your example `c' does not care about its argument and `a' and `b' are neutral with respect to the intended result, then `x.a ().b().c()' is legal But without any additional knowledge every claim of independency of the evaluation order is ungrounded and therefore the proof for the general case "`x.a().b().c()' is illegal" holds. -manfred
Dec 10 2005
In article <Xns9728D4F579A89svv1999hotmailcom 63.105.9.61>, Manfred Nowak says...Ivan Senji wrote: [...]I think the question is how can x.a().b().c() be evaluated in an order OTHER than c(b(a(x)))? The ?.b( ) part can't be executed until the ? part (x.a()) has been determine, and the same for the ?.c() part. Unless I'm way off my rocker, there is no other practicable way to build the code. If I'm wrong please tell me how. p.s. For that matter is this legal? int i = f() + 2*f(); what if this is f(); int f() { static int r = 0; return r++; }Can you please enlighten me and explain why is x.a().b().c() illegal?As stated above `x.a().b().c()' is illegal in the general case. This stems from the definition in the specs: | Unless otherwise specified, the implementation is free to | evaluate the components of an expression in any order. It is an | error to depend on order of evaluation when it is not | specified. Intensified by | Parenthesis control operator precedence, parenthesis do not | control order of evaluation. In `x.a().b().c()' postfix expressions are stringed together for which the order of evaluation is not specified. Therefore its an error to depend on the order of evaluation.
Dec 10 2005
BCS wrote: [...]Sorry to say that to a really intelligent one: you are wrong. Look through the thread and you will find some examples. Most convincing to me is the following observation: If `typeof( x.a().b()).c()' and `typeof( x.a()).b()' are accepted by the compiler without errors, then each of the calls in `x.a().b().c ()' can be executed without the need to first call `a()', then `b()'. This is for example the case when `a', `b' and `c' are `static' struct/union/class members or functions, not delegates.In `x.a().b().c()' postfix expressions are stringed together for which the order of evaluation is not specified. Therefore its an error to depend on the order of evaluation.I think the question is how can x.a().b().c() be evaluated in an order OTHER than c(b(a(x)))? The ?.b( ) part can't be executed until the ? part (x.a()) has been determine, and the same for the ?.c() part. Unless I'm way off my rocker, there is no other practicable way to build the code. If I'm wrong please tell me how.p.s. For that matter is this legal? int i = f() + 2*f(); what if this is f(); int f() { static int r = 0; return r++; }It is not legal if the intended result is the value of the variable `i'. It is legal, if the intended result is to write something to `dout'. -manfred
Dec 10 2005
Egg on my face (ick :-p) this can only be compiled one way: a(b)(c) if the compiler tries from the right, as I suggested earlier, you get the following (with <> indicating groupings) a < (b)(c) > a < b(c) > a ret_b // illegal expression While a (poorly done) compiler might not be able to parse it, it can't generate any other interpretation of it ether. And for something completely different... In article <Xns9728E3BB1149Bsvv1999hotmailcom 63.105.9.61>, Manfred Nowak says...BCS wrote: [...][...]In `x.a().b().c()' postfix expressions are stringed together for which the order of evaluation is not specified. Therefore its an error to depend on the order of evaluation.I think the question is how can x.a().b().c() be evaluated in an order OTHER than c(b(a(x)))?Sorry to say that to a really intelligent one: you are wrong. Look through the thread and you will find some examples. Most convincing to me is the following observation: If `typeof( x.a().b()).c()' and `typeof( x.a()).b()' are accepted by the compiler without errors, then each of the calls in `x.a().b().c ()' can be executed without the need to first call `a()', then `b()'. This is for example the case when `a', `b' and `c' are `static' struct/union/class members or functions, not delegates. -manfredAhhhh., I hadn't considered statics. That makes a lot more sense. I stand corrected. (And thank you!) On the other hand why shouldn't you be able to evaluate ANY typeof even when delegates are used?
Dec 10 2005
BCS wrote: [...]On the other hand why shouldn't you be able to evaluate ANY typeof even when delegates are used?Because delegates are bound to their environment, creating the need to evaluate that environment first and therefore imposing an order of evaluation. Naturally the typeof can be evaluated, but the following call not. -manfred
Dec 10 2005
Manfred Nowak wrote:Ivan Senji wrote: [...]Still i don't agree.Can you please enlighten me and explain why is x.a().b().c() illegal?As stated above `x.a().b().c()' is illegal in the general case.This stems from the definition in the specs: | Unless otherwise specified, the implementation is free to | evaluate the components of an expression in any order. It is an | error to depend on order of evaluation when it is not | specified. Intensified by | Parenthesis control operator precedence, parenthesis do not | control order of evaluation.But this probably refers to the parenthesis in expressions, nut to function call parenthesis.In `x.a().b().c()' postfix expressions are stringed together for which the order of evaluation is not specified. Therefore its an error to depend on the order of evaluation.If this is not defined in the spec than this is an error in the spec. But the fact remains that there is not but one sane order of evaluation. Are you saying this isn't defined:I.e. `x.a().b().c()' is legal only if there is a proof that the order of evaluation does not change the intendedThat would be true in just a few cases.In case of your example you state, that `x.a().b().c()' is equivalent to `c(b(a(x)))'.Yes it is.This seems to indicate, that `a(x)' must be evaluated first. If so, `x.a().b().c()' is illegal according to the specs.OK. If i agree that there is an error in the spec not mentioning that the order of evaluation in this case is defined, will you agree that there is only one meaningful way to evaluate these expressions?If `a(x)' is not required to be evaluated first every try of proof of independeny of evaluation must fail, because nothing is known of the intended result or the impact of the calls on the intended result.But it is required. You cannot call b when you don't know what are you passing to it's first argument.Examples: 1) if `a()', `b()' and `c()' are known to be neutral operationsBut this can't possibly be known, or would be atleast very hard to do in a compiler as there are no const methods.with respect to the intended result, then `x.a().b().c()' is legalYes.2) if in your example `c' does not care about its argument and `a' and `b' are neutral with respect to the intended result, then `x.a ().b().c()' is legalNo. It is legal beacuse it has a well defined (but maybe missing from spec) order of evaluation :)-But without any additional knowledge every claim of independency of the evaluation order is ungrounded and therefore the proof for the general case "`x.a().b().c()' is illegal" holds.My proof wasn't trying to prove independency of the order of evaluation as that would be near impossible, it was mearly trying to proove there is only one sane order.
Dec 10 2005
On Sat, 10 Dec 2005 11:33:39 +0000 (UTC), Manfred Nowak wrote: Manfred, you *must* be having a joke with us. If one has x.a().b().c() how can code for the call of c() be generated until such time as x.a().b() has been evaluated by the compiler? Because to call c() the compiler would have to know the vtable entry for it, and that means it would have to know the class that b() returns. And to know that, it must work out what class a() returns first. Thus the compiler must evaluate this as call the method a() from the vtable of class x. call the method b() from the vtable of the class from the previous call. call the method c() from the vtable of the class from the previous call. There is no other sane way of doing it. It can't call c() before it calls b(), and it can't call b() before it calls a(). This isn't theory its practice. -- Derek Parnell Melbourne, Australia 11/12/2005 12:15:15 AM
Dec 10 2005
Derek Parnell wrote: [...]you *must* be having a joke with us. If one has x.a().b().c() how can code for the call of c() be generated until such time as x.a().b() has been evaluated by the compiler?[...] Derek, Chris Sauls finally got what I was talking of all the time. His words in this thread: "You are right insofar as the types being the important thing at compile time, however the types /are/ reachable in left-associative manner. The /values/ are not, which means the order of evaluation at /runtime/ is different than the associativity, which is a /compile time/ property of expressions, not a /runtime/ one." That means `x.a().b()' must be evaluated only as far as the type of this subexpression is of concern. That can be done by looking at the return types of `a()' and then `b()'. Nothing more has to be done. After that at least the types of `x.a()', `x.a().b()' are on the stack in that order. Then the compiler writer might very well decide to generate the code for the function calls, when popping the types off the stack, thereby defining the evaluation order by `x.a().b().c()', then `x.a().b()' and then `x.a()'. It is also possible, that the compiler assigns different cores for the code generation of the calls and combines the results to a linear code sequence in the order in which the generated fragments return to the main thread. And so on ... -manfred
Dec 10 2005
In article <Xns97289C2827399svv1999hotmailcom 63.105.9.61>, Manfred Nowak says...Derek Parnell wrote: [...]This is completely ridiculous - it cannot evaluate x.a().b().c() without evaluating x.a() and x.a().b() first. Is this a sick joke?you *must* be having a joke with us. If one has x.a().b().c() how can code for the call of c() be generated until such time as x.a().b() has been evaluated by the compiler?[...] Derek, Chris Sauls finally got what I was talking of all the time. His words in this thread: "You are right insofar as the types being the important thing at compile time, however the types /are/ reachable in left-associative manner. The /values/ are not, which means the order of evaluation at /runtime/ is different than the associativity, which is a /compile time/ property of expressions, not a /runtime/ one." That means `x.a().b()' must be evaluated only as far as the type of this subexpression is of concern. That can be done by looking at the return types of `a()' and then `b()'. Nothing more has to be done. After that at least the types of `x.a()', `x.a().b()' are on the stack in that order. Then the compiler writer might very well decide to generate the code for the function calls, when popping the types off the stack, thereby defining the evaluation order by `x.a().b().c()', then `x.a().b()' and then `x.a()'.
Dec 10 2005
MWolf wrote: [...][...] Ooops. I meant "thereby defining the evaluation order by `typeof(x.a().b()).c()', then `typeof(x.a()).b()' and then `x.a()'." You decide whether you want it fast or ordered. If you want it ordered use the provided ordering mechanisms. -manfredthereby defining the evaluation order by `x.a().b().c()', then `x.a().b()' and then `x.a()'.This is completely ridiculous - it cannot evaluate x.a().b().c() without evaluating x.a() and x.a().b() first.
Dec 10 2005
Manfred Nowak wrote:Ooops. I meant "thereby defining the evaluation order by `typeof(x.a().b()).c()', then `typeof(x.a()).b()' and then `x.a()'."The problem with this, is that using typeof() changes the semantics of the expression. In the case of 'typeof(x.a()).b()' you are saying (warning: code to english) "call method b, on the return type of a, defined on x." This is not the same as the Whisper syntax in question, because Whisper works only on object instances, never on types (unless Kris has added a static Whisper notation while I wasn't looking... that would definitely be questionable, IMHO.) Its apples and oranges. Yes it does provide a proof case, but it isn't equivelant to the case in practice. -- Chris Sauls
Dec 10 2005
Manfred Nowak wrote:Chris Sauls wrote: [...]I don't claim any such thing. In fact I'm claiming the /opposite/, which is that the value in this case is impossible to predict perfectly at compile time, which is why the order of operations is in fact fixed. "A" must be evaluated first, because it is an operand of "B" -- so if they are evaluated in any other order, a runtime error is inescapable, because "B" has a void operand (illegal in the mass majority of expressions). I really, truly, just don't see how it could be any other way.Any expression, B, containing any operand whose value is taken from the result of another expression, A, must be evaluated /after/ the expression, A. QED.Why is everyone basing her/his tries of proof on non existing restrictions? Here you claim, that the values of expressions must be known at compile time, which is clearly wrong.The compiler needs only knowledge upon the types of the expressions to generate code for the values given at runtime. This is true for every subexpression also.Exactly.Once the types are known the compiler is free to evaluate all parts of the expression in any order, even at randomSure, insofar as their later runtime evaluation is not dependant on the evaluation of any other expressions. In order for any expression to evaluate ("execute") it must be /complete/, and in this special case we are discussing, that completeness relies on the previous execution of another expression. Again, any compiler that doesn't enforce this, is going to generate useless code.If there is an ambiguity in the type a subexpression can have, the compiler is not forced to detect that ambiguity and report on it. The compiler is allowed to choose out of the ambiguous candidates at random. That means that in the example given by BCS the code fragnment A a2= a+b+c; C c2= a+b+c; may or may not result in reporting compilation errors, which in addition may change between consecutive runs.Could be. But we're discussing the Whisper syntax in Mango.io are we not? So let's stick to that. -- Chris Sauls
Dec 10 2005
Chris Sauls wrote: [...]the value in this case is impossible to predict perfectly at compile time, which is why the order of operations is in fact fixed.[...] Postfix expressions are _not_ left associative and stringed together do _not_ change precedence. Therefore the two calls in `a.b().c()' can be evaluated in any order. This arguments start to circle around an imaginary spot in the far distance. If any value is impossible to predict perfectly at compile time by definition that expression is illegal. If there is an expression E consisting of at least two subexpressions A and B and the value of subexpression B depends on the value computed for subexpression A, then the chain of operators joining both subexpressions together must change precendence somewhere. If there is no change in precedence, then because the lack of defined associativity in D the subexpressions can be evaluated in any order, regardless of any known dependencies. Postfix expressions are _not_ left associative and stringed together do _not_ change precedence. -manfred
Dec 10 2005
Now hold on just a second. Manfred Nowak wrote:If any value is impossible to predict perfectly at compile time by definition that expression is illegal.But yet, in a previous post you stated:Here you claim, that the values of expressions must be known at compile time, which is clearly wrong. The compiler needs only knowledge upon the types of the expressions to generate code for the values given at runtime.So which way is it? Must values be reachable at compile time or not? Clearly, not, just as you asserted previously, and I asserted recently. As for postfix operators being lest-associative... it really just doesn't matter. You are right insofar as the types being the important thing at compile time, however the types /are/ reachable in left-associative manner. The /values/ are not, which means the order of evaluation at /runtime/ is different than the associativity, which is a /compile time/ property of expressions, not a /runtime/ one. It doesn't make any sense any other way. -- Chris Sauls
Dec 10 2005
Chris Sauls wrote: [...]So which way is it? Must values be reachable at compile time or not? Clearly, not, just as you asserted previously, and I asserted recently.Right. I had serious concern in writing about values, because I meant the _way_ how to reach the value instead of any knowledge on the value itself. Imitating the notation of the foregoing post seems to be secure, but it wasn't.As for postfix operators being lest-associative... it really just doesn't matter.Postfix operators are not left associative in D. [...]It doesn't make any sense any other way.Agreed. -manfred
Dec 10 2005
In article <dne6ha$3jp$1 digitaldaemon.com>, Chris Sauls says...BCS wrote:[...][...] I'm not sure where the expression ‘x.a().b().c()' came from, but this (or the explicit use of opCall) eliminates the ambiguities that the whisper syntax introduces. So getting back to that. The ambiguities come from the fact that () can be translated to a function call or just a set of parens. Example from my last post: struct A { int opCall(int); } struct B { B opCall(A); B opCall(int) } A a; B b; int i; b(a)(i); if evaluated from the left, the parens around ‘a' are function call: ( b.opCall(a) ).opCall(i); if evaluated from the right the parens around ‘a' just parens: b.opCall( (a).opCall(i) );// is this b(a)(i); //this int t1 = a.opCall(i) b.opCall(t1); //or this B t2 = b.opCall(a); t2.opCall(i);According to my understanding of the docs, and experience with D, the second one. Really this whole discussion is silly. The chained calls in the Whisper syntax can not be reordered, because each subsequent call is dependant on the previous call's return value. In 100% of cases of: It is guaranteed that `?.opCall(b)` can not possibly be executed before `obj.opCall(a)` because of that little question-mark there, which stands for the return value of `obj.opCall(a)`. Its all really very straightforward... in fact it reminds me of the sort of thing that would've been on a test in my High School C++ class.
Dec 10 2005
"Manfred Nowak" <svv1999 hotmail.com> wrote in message news:Xns9727D45B325B1svv1999hotmailcom 63.105.9.61...Moreover the behaviour of the whisper notation is undefined according to the specs, which clearly state that the order of all expression evaluations are undefined if not explicitely defined---and no definition for the whisper notation is given.I've been intending for a while now to revise that to make the order of evaluation explicit. The undefined order of evaluation does not offer many benefits, and causes a lot of headaches. But there are two different things here: order of evaluation, and operator precedence. D operator precedence is clearly defined. For example: a + b + c is defined to be: (a + b) + c There is no ambiguity. The order of evaluation ambiguity comes from the order a, b, and c are evaluated. For example, for: void a() { writefln("a"); } void b() { writefln("b"); } void c() { writefln("c"); } ... a() + b() + c() may output one of: abc acb bac bca cab cba
Dec 13 2005
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote"MIcroWizard" <MIcroWizard_member pathlink.com> wrote in message news:dnbut2$17pk$1 digitaldaemon.com...~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Yes, that's the general idea. The mango.io package is based partly upon a set of buffered readers and writers. At their heart, the readers and writers support a chaining put/get syntax; similar to how you describe it: class Writer { Writer put (int) {} Writer put (char[]) {} Writer put (dchar[]) {} ... } class Reader { Reader get (inout int) {} Reader get (inout char[]) {} Reader get (inout dchar[]) {} ... } These are aliased both as opCall() and opShl()/opShr(). Obviously, this supports the C++ iostream syntax: Writer write; Reader read; write << 10 << "green bottles"; read >> count >> description. But, more to the point, it supports what we affectionately call "whisper" syntax in Mango: void foo (Writer write, Reader read) { char[] materials; int time, cost; write (time) (cost) (materials); read (time) (cost) (materials); } Some nice aspects of this particular approach are: 1) the syntax is identical for both read & write operations. The compiler takes care of providing the appropriate source or target reference. The only different is the verb used (write vs read). You can use the verbs input/output instead, if that rocks your boat :-) 2) it is thoroughly type-safe. 3) default arguments can be passed as part of the call. 4) it is quite efficient. DMD does a fine job optimizing this style of programming. 5) the approach supports an obvious and natural way to be explicit about transcoding (between char/wchar/wdchar content). 6) it is flexible and highly extensible. For example, the syntax is just the same whether you're working with text or binary I/O 7) an empty set of parenthesis maps quite naturally to a "flush" operation :-) e.g: write (time) (cost) (materials) (); 8) there's nothing that restricts one from using printf() syntax instead. For example, the text oriented DisplayWriter additionally has print() and println() methods. Stdout is an instance of DisplayWriter. 9) it becomes trivial to map a user-class to the I/O system ~ i.e. ============== class ContractJob : IWritable, IReadable { char[] materials; int time, cost; void write (IWriter output) { output (time) (cost) (materials); } void read (IReader input) { input (time) (cost) (materials); } } ContractJob job1; ContractJob job2; write (job1) (job2) (); read (job1) (job2); =============== 10) "whisper" is a cool name :-p You can read more about Mango I/O in the source code, such as here: http://trac.dsource.org/projects/mango/browser/trunk/mango/io/Buffer.d http://trac.dsource.org/projects/mango/browser/trunk/mango/io/Writer.d http://trac.dsource.org/projects/mango/browser/trunk/mango/io/Reader.dI've just seen in one Mango example: Stdout (CR) ("files:") (CR);I haven't used Mango, but I bet it does this: class SomeOutputStream { SomeOutputStream opCall(whatever) { .... // outputs the whatever to the stream buffer return this; // important } }
Dec 09 2005
Kris wrote:Yes, that's the general idea. The mango.io package is based partly upon a set of buffered readers and writers. At their heart, the readers and writers support a chaining put/get syntax; similar to how you describe it: class Writer { Writer put (int) {} Writer put (char[]) {} Writer put (dchar[]) {} ... } class Reader { Reader get (inout int) {} Reader get (inout char[]) {} Reader get (inout dchar[]) {} ... } These are aliased both as opCall() and opShl()/opShr(). Obviously, this supports the C++ iostream syntax: Writer write; Reader read; write << 10 << "green bottles"; read >> count >> description. But, more to the point, it supports what we affectionately call "whisper" syntax in Mango: void foo (Writer write, Reader read) { char[] materials; int time, cost; write (time) (cost) (materials); read (time) (cost) (materials); }I love the "whisper" syntax. However, it's worth noting that it's a tiny bit less flexible than the C++ method. For example, say I want to create a ReaderWriter class. The C++ method is unambiguous, if confusing: ReaderWriter rw = new ReaderWriter(); rw << x >> y; The "whisper" syntax doesn't work in this case, as there's no way to determine whether the user wants to read or write. That said, the need for "IOStreams" is not terribly common in the average case, though I do personally use this technique quite a bit in C++ (stringstreams, for example). But then I'm not terribly familiar with the intricacies of Mango. Is there an easy way around this already? Sean
Dec 09 2005
"Sean Kelly" <sean f4.ca> wrote ...I love the "whisper" syntax. However, it's worth noting that it's a tiny bit less flexible than the C++ method. For example, say I want to create a ReaderWriter class. The C++ method is unambiguous, if confusing: ReaderWriter rw = new ReaderWriter(); rw << x >> y; The "whisper" syntax doesn't work in this case, as there's no way to determine whether the user wants to read or write. That said, the need for "IOStreams" is not terribly common in the average case, though I do personally use this technique quite a bit in C++ (stringstreams, for example). But then I'm not terribly familiar with the intricacies of Mango. Is there an easy way around this already?Mango.io does that by sharing the buffer between reader and writer: auto read = new Reader (commonBuffer); auto write = new Writer (commonBuffer); write (x); // or write << x; read (y); // or read >> y; It's a bit more explicit than the C++ version. One might also use the Buffer by itself for such things, using append() and get() methods, sans any formatting.
Dec 09 2005
While I must concur with Kris and Derek that the whisper syntax is unambiguous, I disagree with its philosophy. In article <dnck45$2sdj$1 digitaldaemon.com>, Kris says...These are aliased both as opCall() and opShl()/opShr(). Obviously, this supports the C++ iostream syntax: Writer write; Reader read; write << 10 << "green bottles"; read >> count >> description.One of the best things about D is type safe variadic arguments. Do we really need to go back to the bad old days of C++? writef/readf accomplish all the goals of stream insertion, but do not suffer from the lack of atomicity. Consider the following example, Thread 1: write ("Thread 1:") ('a') ('b') ('c'); Thread 2: write ("Thread 2:") (1) (2) (3); The output could be any number of the following: Thread 1:Thread2:12ab3c Thread 2:12Thread 1:abc3 Thread 1:abThread 2:1c23 .. Now look at this example, Thread 1: writefln("Thread 1: %s %s %s", 'a', 'b', 'c'); Thread 2: writefln("Thread 2: %d %d %d", 1, 2, 3); The output is either Thread 1: a b c Thread 2: 1 2 3 --- or --- Thread 2: 1 2 3 Thread 1: a b c This makes the output much simpler, since writef can be synchronized. Moreover, writef and readf make it easier to specify things like formatting. Just add some format args, and you can spitout hex output or binary just as easily as decimal. Ultimately, I believe the whisper syntax should be avoided since it seems redundant and error prone, not mention incongruous with the D style.
Dec 10 2005
"mclysenk" <mclysenk_member pathlink.com> wrote ...While I must concur with Kris and Derek that the whisper syntax is unambiguous, I disagree with its philosophy.~~~~~~~~~~~~~~~~ Phew ~ a rational perspective! Do you mind if I change the subject-title? You make a good point about the atomicity of call-chaining vs variadic arguments. Please permit me to offer an alternate viewpoint?One of the best things about D is type safe variadic arguments. Do we really need to go back to the bad old days of C++?As noted before, mango.io was around long before typesafe variadic args. Mango.io was built like it is to explicitly gain type-safety where it was lacking before. Variadic typeinfo is great, yet extracting the actual type is not particularly efficient. More importantly, variadic typeinfo still only works reasonably for output; not input. Sean will attest to this, as he's done in the past ~ it's awkward to write an scanf() using typeinfo as it stands ... still requires pointers from the user. On the other hand, the Whisper notation is identical from the user perspective, for both input and output operation. It is always completely explicit about type, at compile-time. I feel the latter is important ~ other don't, and that's perfectly fine.writef/readf accomplish all the goals of stream insertion, but do not suffer from the lack of atomicity. Consider the following example,The scenario describes a type of race-condition; where two or more threads contend for a shared resource. In this particular case, it's the console. I don't have to tell you that race-conditions usually have to be explicitly managed in one way or another, so I won't. Instead, I'll just note that mango.io is explicit about shared data ~ it never hides anything internally that might be shared between threads, thus requiring that all possible shared entities be provided by the programmer (such as a buffer). This is pretty self-evident, yet is often overlooked. BTW: this atomicity concern refers to phobos.Stream just as much as it applies to mango.io ~~~~~~~~~~~~~~~~~~ So, what about atomicity? Does writefln() have some kind of an advantage over call-chaining, aka whisper notation? The answer is both yes and no. The notion that writefln() is atomic is only partly true ~ for one thing it is not synchronized. But that aside, it depends on what you want to call atomic. For instance: writefln("header info %? %?", blah, foo); // create some output data writefln("content: %", wumpus); // now create a footer writefln("footer info %? %?", wombat, arff); That's a contrived example, but the point is one of scope: how much atomicity is expected at any given time? What the user should do, where multiple threads are contending, is to be explicit about atomicity. Thus, it might be something like this contrived example: synchronized (someGlobalLock) { writefln("header info %? %?", blah, foo); // create some output data writefln("content: %", wumpus); // now create a footer writefln("footer info %? %?", wombat, arff); } or some variation upon that. You see what I'm getting at? The atomicty of writefln() is really in the eye of the beholder ... even assuming it were itself synchronized at the function level. This is, I imagine, why writefln() does not synchronize (at least, I doubt that it does, and you impled it doesn't). It is certainly why mango.io does not synchronize ~ would be just a waste of cycles, and is noted in the Stdout documentation. ~~~~~~~~~~~~~~~~~~~~~~ So what does mango.io do about this? Well, by design, it does support this kind of approach: synchronized (Stdout) { Stdout (blah blah blah); Stdout (blah blah blah); } At least there's a known, common synch point if you really, really need to do that kind of thing :-) Mango.io also has a Print() object, which you can alias as writefln() if you like: Print ("%d green bottles", 10); // uses variadic args! or even this: synchronized (Print) { Print ("%d green bottles", 10); Print ("hanging on the wall"); } Please note that Stdout and Print are merely static object wrappers upon a flexible foundation. The mango.log package provides yet another alternative: auto log = Logger.getLogger ("my.logger.name"); log.trace ("some kind of formatted " ~ "message"); If the logger is configured for the console, the output there will be "atomic". Of course, you can also do this kind of thing: auto sprint = new Sprint (1024); log.info (sprint ("%d green bottles", 10)); Mango.convert has both struct and class based formatting; all completely thread-safe; and all fully Unicode aware. More so than Phobos, and faster too, for what that's worth :-) (note: mango formatting avoids the writef() issue of extracting format chars from each string). Mango.log has far more interesting logging facilities that just the console, but it's noted here for the purposes of atomic comparison. ~~~~~~~~~~~~~~~~~~~~~~~~Moreover, writef and readf make it easier to specify things like formatting. Just add some format args, and you can spitout hex output or binary just as easily as decimal.Very true. This is why mango.io is a multi-level design. The whisper notation is intended to provide general purpose I/O for text and binary data. Mango.convert has all the printf() like facilities, which are then wrapped in a number of different ways for both classes and structs (the latter are great when you need to avoid the heap): Sprint ~ binds a formatter to a char/wchar/dchar array. Format ~ binds a delegate-trio to a formatter. The formatting is bound to the console in two alternate ways, so you can choose what feels comfortable: Print ~ binds a formatter to the console Stdout ~ binds a formatter and a Whisper-writer to the console. Additionally, mango.convert will *never*, itself, touch the heap. Never. From a throughput or server standpoint, that would be tantamount to criminal activity :-) ~~~~~~~~~~~~~~~~~~~~~~Ultimately, I believe the whisper syntax should be avoided since it seems redundant and error prone,I'm afraid I cannot agree. Surprise ;-) It's not redundant since it's more efficient that varargs, it works very cleanly for input as well as output, it catches type-errors at compile-time, and all the other 10 reasons I gave a couple of days back :-) It's not error-prone. At least, not in the way you describe ~ which was about race conditions on the console. We can split hairs about what constitutes a level of atomicity all day, but in the end mango.io is certainly no worse off than Phobos in that regard. You can use Print if you prefer, or better yet, use mango.log instead! The latter will send your application output across the Internet if you like (to Chainsaw). Plus, you can dynamically configure the output of a running application via the web-based Log Manager. That's really a very powerful combination. Perhaps now that the intermittant cyclic-static-ctor thing appears to have been worked around, more people will actually give it a whirl? ~~~~~~~~~~~~~~~~~~~~~~~~not mention incongruous with the D style.As to being incongruous to D style ~ I think that's perhaps a bit subjective? I really like having choice and flexibility in a library. I personally like the clear, simple, and obvious symmetry of Whisper: output (time) (cost) (materials) (); input (time) (cost) (materials); I like some of the extensibility aspects too. It's horses-for-courses, wouldn't you agree? ~~~~~~~~~~~~~~~~~~~~~~~~~~ I'll just add that mango.io was built originally for high-throughput in a highly-threaded environment ~ t'was built very-much with threads in mind. The Phobos I/O at the time was not even close to being applicable for what was needed. And to this day it's still, ahh, uncohesive. Phobos is great for some folks. Mango is there for others, if they wish to use it. ~~~~~~~~~~~~~~~~~~~~~~~~~ Thanks for the provocative points, mclysenk. I do appreciate it, and am well aware that mango.io is not for everyone :-) - Kris
Dec 10 2005
Kris wrote:So, what about atomicity? Does writefln() have some kind of an advantage over call-chaining, aka whisper notation? The answer is both yes and no. The notion that writefln() is atomic is only partly true ~ for one thing it is not synchronized. But that aside, it depends on what you want to call atomic. For instance:[snip]or some variation upon that. You see what I'm getting at? The atomicty of writefln() is really in the eye of the beholder ... even assuming it were itself synchronized at the function level. This is, I imagine, why writefln() does not synchronize (at least, I doubt that it does, and you impled it doesn't).I think it is atomic at the function level, though mostly as a side-effect. Multiple successive calls to putchar are slow because each call has to lock the output stream to ensure stream integrity. For this reason, writef locks the output stream at the outset and holds the lock until the call completes. I believe this should provide call atomicity, though another implementation is not required to emulate this behavior.It is certainly why mango.io does not synchronize ~ would be just a waste of cycles, and is noted in the Stdout documentation.Agreed. Speculative locking in library code is generally pointless, as it rarely matches actual usage patterns. The only exception IMO is access to any shared static data, as that is not typically something the user is aware of or can control externally. This is the general thread-safety level of STL implementations.I have mixed feelings about OO-based formatted IO. It can be quite nice in structured applications, but tends to be more complex in ad-hoc situations. Still, Mango is quite a bit more flexible than writef and that it doesn't allocate memory is a huge bonus. Personally, I'd like to have both options available.Moreover, writef and readf make it easier to specify things like formatting. Just add some format args, and you can spitout hex output or binary just as easily as decimal.Very true. This is why mango.io is a multi-level design. The whisper notation is intended to provide general purpose I/O for text and binary data. Mango.convert has all the printf() like facilities, which are then wrapped in a number of different ways for both classes and structs (the latter are great when you need to avoid the heap): Sprint ~ binds a formatter to a char/wchar/dchar array. Format ~ binds a delegate-trio to a formatter. The formatting is bound to the console in two alternate ways, so you can choose what feels comfortable: Print ~ binds a formatter to the console Stdout ~ binds a formatter and a Whisper-writer to the console. Additionally, mango.convert will *never*, itself, touch the heap. Never. From a throughput or server standpoint, that would be tantamount to criminal activity :-)I'll just add that mango.io was built originally for high-throughput in a highly-threaded environment ~ t'was built very-much with threads in mind. The Phobos I/O at the time was not even close to being applicable for what was needed. And to this day it's still, ahh, uncohesive. Phobos is great for some folks. Mango is there for others, if they wish to use it.Agreed. While I think readf/writef is a great general purpose tool, it's Mango I'd use for serious work. That said, my line of work is exactly what Mango was designed for so perhaps I'm a bit biased :-) Sean
Dec 10 2005
Some minor points: "Sean Kelly" <sean f4.ca> wrote...I've always felt that writable globals were terribly poor-form in threaded environments. Encapsulation allows one to eliminate such things quite nicely ~ something that D truly excels at by providing aggregate-style structs. But, I suppose there are always limits?It is certainly why mango.io does not synchronize ~ would be just a waste of cycles, and is noted in the Stdout documentation.Agreed. Speculative locking in library code is generally pointless, as it rarely matches actual usage patterns. The only exception IMO is access to any shared static data, as that is not typically something the user is aware of or can control externally. This is the general thread-safety level of STL implementations.Agreed on all counts. I'd like to clarify that mango.convert does have struct-based Format and Sprint ~ you just place them on the stack and call the ctor() function. There's an example here, at line 90: http://trac.dsource.org/projects/mango/browser/trunk/mango/convert/Rfc1123.dSprint ~ binds a formatter to a char/wchar/dchar array. Format ~ binds a delegate-trio to a formatter. The formatting is bound to the console in two alternate ways, so you can choose what feels comfortable: Print ~ binds a formatter to the console Stdout ~ binds a formatter and a Whisper-writer to the console. Additionally, mango.convert will *never*, itself, touch the heap. Never. From a throughput or server standpoint, that would be tantamount to criminal activity :-)I have mixed feelings about OO-based formatted IO. It can be quite nice in structured applications, but tends to be more complex in ad-hoc situations. Still, Mango is quite a bit more flexible than writef and that it doesn't allocate memory is a huge bonus. Personally, I'd like to have both options available.
Dec 10 2005
Still, Mango is quite a bit more flexible than writef and that it doesn't allocate memory is a huge bonus. Personally, I'd like to have both options available.I can't see where writef allocates memory - though I haven't looked all that hard. Can someone point out what use cases allocate? Also what flexibility are you referring to?
Dec 10 2005
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message news:dnftfm$3mv$1 digitaldaemon.com...Ben; I don't think Sean actually said that writef() did? I can point you to some areas that do though: just did a grep through phobos for 'new' and the toString() functions are notable in this respect ~ these are often used instead of sprintf() to get good throughput where sprintf() might be considered overkill? Adding an HTTP header comes to mind? These toString() functions always tend to allocate from the heap, as does the date formatter ~ again something that's used heavily in some kinds of application. There's one notable exception in toString() where it does not allocate. Here's the code: /// ditto char[] toString(uint u) { char[uint.sizeof * 3] buffer = void; int ndigits; char c; char[] result; ndigits = 0; if (u < 10) // Avoid storage allocation for simple stuff result = digits[u .. u + 1]; [snip] return result; } See that ~ it returns a writable reference to the shared digit-map. Seems like a good reason to have read-only arrays? These are all things that can be easily fixed.Still, Mango is quite a bit more flexible than writef and that it doesn't allocate memory is a huge bonus. Personally, I'd like to have both options available.I can't see where writef allocates memory - though I haven't looked all that hard. Can someone point out what use cases allocate?
Dec 10 2005
"Kris" <fu bar.com> wrote in message news:dng0ea$66g$1 digitaldaemon.com..."Ben Hinkle" <ben.hinkle gmail.com> wrote in message news:dnftfm$3mv$1 digitaldaemon.com...Oh - I thought "is a huge bonus" was meant to contrast with writef.Ben; I don't think Sean actually said that writef() did?Still, Mango is quite a bit more flexible than writef and that it doesn't allocate memory is a huge bonus. Personally, I'd like to have both options available.I can't see where writef allocates memory - though I haven't looked all that hard. Can someone point out what use cases allocate?I can point you to some areas that do though: just did a grep through phobos for 'new' and the toString() functions are notable in this respect ~ these are often used instead of sprintf() to get good throughput where sprintf() might be considered overkill? Adding an HTTP header comes to mind? These toString() functions always tend to allocate from the heap, as does the date formatter ~ again something that's used heavily in some kinds of application.I agree toString typically allocates memory. I don't really follow the comparison with sprintf and adding HTTP headers, though. If the date formatter allocates memory too often then I suggestion someone propose and/or submit some updates to the date formatter to improve it. That should be independent of the I/O system or writef/whispering. For example I vaguely remember suggesting various functions that return strings be passed an optional char[] to fill (somewhat like std.stream.Stream.readLine does).There's one notable exception in toString() where it does not allocate. Here's the code: /// ditto char[] toString(uint u) { char[uint.sizeof * 3] buffer = void; int ndigits; char c; char[] result; ndigits = 0; if (u < 10) // Avoid storage allocation for simple stuff result = digits[u .. u + 1]; [snip] return result; } See that ~ it returns a writable reference to the shared digit-map. Seems like a good reason to have read-only arrays?oh no - here we go... readonly arrays again. ;-)These are all things that can be easily fixed.ok. I'm sure people would be willing to listen to proposals about making toString (or toString-like functions) more efficient. I think that came up a few times during the COW/const/in-place threads.
Dec 10 2005
"Ben Hinkle" <ben.hinkle gmail.com> wroteSimply noted that one is often used in place of the other. Both sprintf() and toString() are part of a "package" to convert from 'type' to string: number to string, time to string, and so one. If one needed, for example, to convert a number to a string then one would use either sprintf() or toString(). They're related by "intent" or usage. Was the implication otherwise?I can point you to some areas that do though: just did a grep through phobos for 'new' and the toString() functions are notable in this respect ~ these are often used instead of sprintf() to get good throughput where sprintf() might be considered overkill? Adding an HTTP header comes to mind? These toString() functions always tend to allocate from the heap, as does the date formatter ~ again something that's used heavily in some kinds of application.I agree toString typically allocates memory. I don't really follow the comparison with sprintfoh no - here we go... readonly arrays again. ;-)Nope ... but then, do you find it acceptable the D library returns access to shared internals? ;-)
Dec 10 2005
My opinions were expressed in the previous threads. In case other people are wondering what threads those are check out the archives during June/July with titles involving words like COW, immutable, readonly and the Round I to VII threads. I also found the thread I started about passing optional scratch buffers to toString: http://www.digitalmars.com/d/archives/digitalmars/D/28249.html There were no replies so I assumed people were happy with the GC impact of std.string and friends.oh no - here we go... readonly arrays again. ;-)Nope ... but then, do you find it acceptable the D library returns access to shared internals? ;-)
Dec 11 2005
Ben Hinkle wrote:I don't think writef does allocate memory, though Object.toString() is likely to do so, which is how writef prints objects. So far as flexibility is concerned, writef is great for writing formatted data to the console, to a file, or to another string, but it's fairly common in large applications that I will want to control formatting of objects based on stream characteristics, handle parsing of input data in a similar fashion, make writing to a socket the same as writing to a file (which I suppose it is in Unix), etc. While this can all be accomplished via writef/readf, it's not really what they were designed for IMO. But writef is my tool of choice for console IO and such. You just can't beat the usability of a one-line function call, which Mango doesn't allow out of the box (though wrappers could do this easily enough). SeanStill, Mango is quite a bit more flexible than writef and that it doesn't allocate memory is a huge bonus. Personally, I'd like to have both options available.I can't see where writef allocates memory - though I haven't looked all that hard. Can someone point out what use cases allocate? Also what flexibility are you referring to?
Dec 11 2005
"Sean Kelly" <sean f4.ca> wrote in... [snip]for IMO. But writef is my tool of choice for console IO and such. You just can't beat the usability of a one-line function call, which Mango doesn't allow out of the box (though wrappers could do this easily enough).That's a small misconception, since mango.io does have out-of-the-box one-line functions for console support. There's were a number of examples earlier, though they likely were lost amid the sea of unrest :-)
Dec 11 2005
Kris wrote:"Sean Kelly" <sean f4.ca> wrote in... [snip]Ah nice. I only read a few of the posts in the last thread :-) Seanfor IMO. But writef is my tool of choice for console IO and such. You just can't beat the usability of a one-line function call, which Mango doesn't allow out of the box (though wrappers could do this easily enough).That's a small misconception, since mango.io does have out-of-the-box one-line functions for console support. There's were a number of examples earlier, though they likely were lost amid the sea of unrest :-)
Dec 11 2005
"Sean Kelly" <sean f4.ca> wrote ...Ah nice. I only read a few of the posts in the last thread :-)(* splutter *) :-)
Dec 11 2005