www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - What is it? Syntax sugar or some stupid lazyness?

reply MIcroWizard <MIcroWizard_member pathlink.com> writes:
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
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"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
next sibling parent reply John Demme <me teqdruid.com> writes:
Jarrett Billingsley wrote:

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

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 Demme
Dec 09 2005
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
next sibling parent reply pragma <pragma_member pathlink.com> writes:
In article <Xns9727D45B325B1svv1999hotmailcom 63.105.9.61>, Manfred Nowak
says...
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

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: # Stdout (CR) ("hello") (" ") ("world") (CR); Aliases to nothing other than: # Stdout.opCall(CR).opCall("hello").opCall(" ").opCall("world").opCall(CR); 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 yahoo
Dec 09 2005
next sibling parent Sean Kelly <sean f4.ca> writes:
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:
 # Stdout (CR) ("hello") (" ") ("world") (CR);
 
 Aliases to nothing other than:
 # Stdout.opCall(CR).opCall("hello").opCall(" ").opCall("world").opCall(CR);

Agreed. Where does expression ordering ambiguity come into play here? Sean
Dec 09 2005
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 9 Dec 2005 20:22:59 +0000 (UTC), pragma wrote:

 To me, the line:
 # Stdout (CR) ("hello") (" ") ("world") (CR);
 
 Aliases to nothing other than:
 # Stdout.opCall(CR).opCall("hello").opCall(" ").opCall("world").opCall(CR);

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
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
parent reply "Kris" <fu bar.com> writes:
"Manfred Nowak" <svv1999 hotmail.com> wrote
 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*

~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Manfred Nowak wrote:
 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.

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. Sean
Dec 09 2005
parent Manfred Nowak <svv1999 hotmail.com> writes:
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
prev sibling parent reply "Kris" <fu bar.com> writes:
"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
parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 9 Dec 2005 22:52:16 -0800, Kris wrote:

 "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?

Sorry but I don't know what Manfred is talking about either.
 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
parent "Kris" <fu bar.com> writes:
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>...
 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?

Sorry but I don't know what Manfred is talking about either.
 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
prev sibling next sibling parent reply "Kris" <fu bar.com> writes:
"Manfred Nowak" <svv1999 hotmail.com> wrote
 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.

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
next sibling parent reply Derek Parnell <derek psych.ward> writes:
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
next sibling parent reply BCS <BCS_member pathlink.com> writes:
In article <gf10ceogj4nu.1k0swchr1kpfj.dlg 40tude.net>, Derek Parnell says...
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

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??
Dec 09 2005
next sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
parent "Kris" <fu bar.com> writes:
"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
prev sibling parent reply "Walter Bright" <newshound digitalmars.com> writes:
"BCS" <BCS_member pathlink.com> wrote in message
news:dnctp1$k9k$1 digitaldaemon.com...
 In article <gf10ceogj4nu.1k0swchr1kpfj.dlg 40tude.net>, Derek Parnell

I guess that a statement such as

    b + c + d;

could evaluate to

    b.opAdd(c).opAdd(d)


Operator precedence requires this evaluation.
or

    d.opAdd(c).opAdd(b)


No.
but once the evaluation was set, the execution would proceed in left to
right order.


No, *that* is currently undefined. It is currently undefined whether b, c, or d is evaluated first. The operator precedence, however, *is* nailed down.
 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

Correct.
 auto c2 = (a + b) + c;  // c2 is C

This 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
parent reply BCS <BCS_member pathlink.com> writes:
Walter Bright wrote:
 "BCS" <BCS_member pathlink.com> wrote in message
 news:dnctp1$k9k$1 digitaldaemon.com...
 
In article <gf10ceogj4nu.1k0swchr1kpfj.dlg 40tude.net>, Derek Parnell

says...
I guess that a statement such as

   b + c + d;

could evaluate to

   b.opAdd(c).opAdd(d)


Operator precedence requires this evaluation.
or

   d.opAdd(c).opAdd(b)


No.
but once the evaluation was set, the execution would proceed in left to
right order.


No, *that* is currently undefined. It is currently undefined whether b, c, or d is evaluated first. The operator precedence, however, *is* nailed down.
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

Correct.
auto c2 = (a + b) + c;  // c2 is C

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

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?
Dec 13 2005
parent reply "Walter Bright" <newshound digitalmars.com> writes:
"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
parent reply BCS <BCS_member pathlink.com> writes:
Walter Bright wrote:
 "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.

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 )
Dec 13 2005
next sibling parent reply xs0 <xs0 xs0.com> writes:
BCS wrote:
 Walter Bright wrote:
 
 "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.

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 )

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
Dec 13 2005
parent BCS <BCS_member pathlink.com> writes:
xs0 wrote:
 BCS wrote:
 
 Walter Bright wrote:

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

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 )

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

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 ."
Dec 13 2005
prev sibling parent Sean Kelly <sean f4.ca> writes:
BCS wrote:
 Walter Bright wrote:
 "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.

I just reread that, and I don't see it.

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
Dec 13 2005
prev sibling parent reply Bruno Medeiros <daiphoenixNO SPAMlycos.com> writes:
Derek Parnell wrote:
 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)

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."
Dec 09 2005
next sibling parent Sean Kelly <sean f4.ca> writes:
Bruno Medeiros wrote:
 Derek Parnell wrote:
 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)

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.

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]; Sean
Dec 09 2005
prev sibling parent reply Bruno Medeiros <daiphoenixNO SPAMlycos.com> writes:
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++. 

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
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
parent reply Bruno Medeiros <daiphoenixNO SPAMlycos.com> writes:
Manfred Nowak wrote:
 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?

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."
Dec 10 2005
parent Manfred Nowak <svv1999 hotmail.com> writes:
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
prev sibling next sibling parent Sean Kelly <sean f4.ca> writes:
Kris wrote:
 "Manfred Nowak" <svv1999 hotmail.com> wrote
 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.

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.

I 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. Sean
Dec 09 2005
prev sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
parent reply Bruno Medeiros <daiphoenixNO SPAMlycos.com> writes:
Manfred Nowak wrote:
 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 ;-)

choose a different order, which would yield different program results. That is no optimization, and such ambiguous code would be useless.
 
 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.
 

-- Bruno Medeiros - CS/E student "Certain aspects of D are a pathway to many abilities some consider to be... unnatural."
Dec 09 2005
parent reply BCS <BCS_member pathlink.com> writes:
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
next sibling parent reply James Dunne <james.jdunne gmail.com> writes:
BCS wrote:
 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);

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.
Dec 09 2005
parent Manfred Nowak <svv1999 hotmail.com> writes:
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
prev sibling next sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
parent reply "Kris" <fu bar.com> writes:
"Manfred Nowak" <svv1999 hotmail.com> wrote...
 BCS wrote:

 [...]
 as to the evaluation of the "wisper syntax" how about the
 following

Wohoo! Second strike. You are really clever!

And you, my friend, are quite foolish, trolling, or both.
Dec 10 2005
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
next sibling parent "Kris" <fu bar.com> writes:
(*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
prev sibling parent Derek Parnell <derek psych.ward> writes:
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.
 
 -manfred

I 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
prev sibling next sibling parent reply "Kris" <fu bar.com> writes:
"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
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
next sibling parent reply "Kris" <fu bar.com> writes:
"Manfred Nowak" <svv1999 hotmail.com>
 Kris wrote:

 [...]
 Does this answer your question, or were you getting at something
 else?

Yes.

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';
 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
next sibling parent Manfred Nowak <svv1999 hotmail.com> writes:
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
prev sibling parent BCS <BCS_member pathlink.com> writes:
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
prev sibling parent Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Manfred Nowak wrote:
 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?

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); ?
Dec 10 2005
prev sibling parent reply Chris Sauls <ibisbasenji gmail.com> writes:
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: # obj (a) (b) (c) ; 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
next sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
next sibling parent Sean Kelly <sean f4.ca> writes:
Manfred Nowak wrote:
 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?

See my comment about postfix expressions. I would be surprised if D were different than C++ insofar as this is concerned. Sean
Dec 10 2005
prev sibling next sibling parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Manfred Nowak wrote:
 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.

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.
Dec 10 2005
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
next sibling parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Manfred Nowak wrote:
 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

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.
Dec 10 2005
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Manfred Nowak wrote:
 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?

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 :)
Dec 10 2005
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Manfred Nowak wrote:
 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.

Can you please enlighten me and explain why is x.a().b().c() illegal?
Dec 10 2005
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
next sibling parent reply BCS <BCS_member pathlink.com> writes:
In article <Xns9728D4F579A89svv1999hotmailcom 63.105.9.61>, Manfred Nowak
says...
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 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++; }
Dec 10 2005
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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)))? 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.

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.
 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
parent reply BCS <BCS_member pathlink.com> writes:
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.


-manfred

Ahhhh., 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
parent Manfred Nowak <svv1999 hotmail.com> writes:
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
prev sibling parent Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Manfred Nowak wrote:
 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.

Still i don't agree.
 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 intended 

That 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 operations 

But 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 legal

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

No. 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
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
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
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
parent reply MWolf <MWolf_member pathlink.com> writes:
In article <Xns97289C2827399svv1999hotmailcom 63.105.9.61>, Manfred Nowak
says...
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()'.

evaluating x.a() and x.a().b() first. Is this a sick joke?
Dec 10 2005
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
MWolf wrote:

[...]
 thereby defining the evaluation order
 by `x.a().b().c()', then `x.a().b()' and then `x.a()'.

x.a().b().c() without evaluating x.a() and x.a().b() first.

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. -manfred
Dec 10 2005
parent Chris Sauls <ibisbasenji gmail.com> writes:
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
prev sibling parent reply Chris Sauls <ibisbasenji gmail.com> writes:
Manfred Nowak wrote:
 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.

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

Sure, 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
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
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
parent reply Chris Sauls <ibisbasenji gmail.com> writes:
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
parent Manfred Nowak <svv1999 hotmail.com> writes:
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
prev sibling parent BCS <BCS_member pathlink.com> writes:
In article <dne6ha$3jp$1 digitaldaemon.com>, Chris Sauls says...
BCS wrote:

 // 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: # obj (a) (b) (c) ; 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.

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) );
Dec 10 2005
prev sibling parent "Walter Bright" <newshound digitalmars.com> writes:
"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
prev sibling parent reply "Kris" <fu bar.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote
 "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 } }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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.d
Dec 09 2005
next sibling parent reply Sean Kelly <sean f4.ca> writes:
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
parent "Kris" <fu bar.com> writes:
"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
prev sibling parent reply mclysenk <mclysenk_member pathlink.com> writes:
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
parent reply "Kris" <fu bar.com> writes:
"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
parent reply Sean Kelly <sean f4.ca> writes:
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:

 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.
 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 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.
 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
next sibling parent "Kris" <fu bar.com> writes:
Some minor points:

"Sean Kelly" <sean f4.ca> wrote...
 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'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?
 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 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.

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.d
Dec 10 2005
prev sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
  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
next sibling parent reply "Kris" <fu bar.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
news:dnftfm$3mv$1 digitaldaemon.com...
  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?

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.
Dec 10 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"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...
  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?

Ben; I don't think Sean actually said that writef() did?

Oh - I thought "is a huge bonus" was meant to contrast with writef.
 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
parent reply "Kris" <fu bar.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote
 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

Simply 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?
 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 10 2005
parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
 oh no - here we go... readonly arrays again. ;-)

Nope ... but then, do you find it acceptable the D library returns access to shared internals? ;-)

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.
Dec 11 2005
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Ben Hinkle wrote:
  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?

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). Sean
Dec 11 2005
parent reply "Kris" <fu bar.com> writes:
"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
parent reply Sean Kelly <sean f4.ca> writes:
Kris wrote:
 "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 :-)

Ah nice. I only read a few of the posts in the last thread :-) Sean
Dec 11 2005
parent "Kris" <fu bar.com> writes:
"Sean Kelly" <sean f4.ca> wrote ...
 Ah nice.  I only read a few of the posts in the last thread :-)

(* splutter *) :-)
Dec 11 2005