www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - overloading operators for I/O

reply Walter Bright <newshound digitalmars.com> writes:
Michiel wrote:
 And we should keep on frowning on attempts to use overloaded << for I/O
 purposes <g>.
Why? I think it's intuitive. The arrows point from the source of the
message to
 the destination. Should the operator only be used for shifting 
because that
 happens to have been its first purpose?

 I also like how you can send a message to an abstract object. And 
that can be a
 cout, a cerr, an error console in a GUI, or something else. Same 
thing the other
 way around.
Good question. C++ conventional wisdom is that's great, because it's always been done that way in C++. Few question it. Let's step back a bit. Suppose I overload '+' to be "launch nuclear missiles." You see some code: x = a + b; and what do you think? I think "add a and b, assign the result to x", but instead, nuclear missiles are launched. It is completely unexpected and unintuitive for '+' to mean anything other than 'add'. To do something completely unrelated to 'add', one should instead write: x = launchNuclearMissiles(a, b); where the meaning is plain, or at least, one isn't assuming it's adding a and b. So why overload operators at all? For the purposes of implementing UDTs (User Defined Types) that have legitimate arithmetic operations on them, such as matrices, extended precision numbers, etc. Now back to '<<' and '>>'. To me, they are arithmetic operators, and mean shift left and shift right. Shift is not an I/O operation. To say they are intuitive is not because they are, but because C++ users are simply accustomed to it. a << b << c << d; That doesn't scream I/O to me. And if a, b, etc. are non-trivial expressions, they wind up: 1) interacting with the operator precedence of << in unfortunate ways 2) suppose you want to print a variable shifted left by a few bits: a << (b << 3) << c << d; blech. 3) Suppose you've got some relational operators and templates thrown in: a << b<c<3>> << c < d << e; and things just go downhill from there. 4) Operators aren't very greppable, meaning it's hard to go grepping through source code looking to see if << has been overloaded. 5) The Spirit library for C++ uses operator overloading to create a specialized DSL for specifying grammar. The trouble is, the code looks exactly like regular C++ expressions (since new operators cannot be defined, nor can operator precedence be changed), and there are some big warning boxes that the resulting appearance may LOOK like C++ but is about as far removed from it as one can get. This doesn't enhance readability. And to sum up: writefln(a, b, c, d); seems pretty intuitive to me. There's just no advantage to overloading <<.
Feb 14 2007
next sibling parent reply Michiel <nomail hotmail.com> writes:
 Good question. C++ conventional wisdom is that's great, because it's
 always been done that way in C++. Few question it.
Sure. I can't deny that I'm used to it because of C++.
 Let's step back a bit.

 <nuclear missile example>
+ is intuitively addition because it's been that way in mathematics long before there were ever computers. Bitshifts, on the other hand, are a bit more recent. And using the << operator for them even more so. It might have been the << operator's first use, but not necessarily its best. << is an arrow. When you point an arrow from a to b, I assume that either a is going to b, or that a becomes b. Not that a is shifted right by b bits.
 So why overload operators at all? For the purposes of implementing UDTs
 (User Defined Types) that have legitimate arithmetic operations on them,
 such as matrices, extended precision numbers, etc.
I totally agree with you here. For example, the way you implemented the equality/inequality and comparison operators is great. Much less room for misuse. And in C++, I never deviated from that behavior.
 <some more examples>
Some good points (though you were using C++ templates as an example, not D templates). But I could make the same argument for writefln: writefln(add(5, 7, 8, pow(42, 3)), f("one", "two", "three, four")); That's not very clear to me either. People will just have to make their own code a bit nicer and use proper spacing or what have you.
 And to sum up:

  writefln(a, b, c, d);

 seems pretty intuitive to me. There's just no advantage to overloading <<.
What about the other reason I mentioned? About sending messages to abstract objects with a standard operator? "I also like how you can send a message to an abstract object. And that can be a cout, a cerr, an error console in a GUI, or something else. Same thing the other way around." I even missed an important one. File I/O. So if << isn't the right streaming operator, use another one. Like <- and ->. You don't use them anyway. To sum up: I love D. But I also like the streaming operator.
Feb 14 2007
next sibling parent Manfred Nowak <svv1999 hotmail.com> writes:
Michiel wrote
 << is an arrow.
This is as true as "[", "(" and "{" are arrows. As a born french speaker one would "<<" primarily recognise as an opening parentheses: http://www.microsoft.com/typography/developers/fdsspec/punc.htm -manfred
Feb 14 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Michiel wrote:
 What about the other reason I mentioned? About sending messages to abstract
 objects with a standard operator?
 
 "I also like how you can send a message to an abstract object. And that can be
a
 cout, a cerr, an error console in a GUI, or something else. Same thing the
other
 way around."
 
 I even missed an important one. File I/O.
This can be done using variadic functions or tuples.
 To sum up: I love D. But I also like the streaming operator.
I didn't expect to convince you <g>.
Feb 14 2007
next sibling parent reply Michiel <nomail hotmail.com> writes:
 This can be done using variadic functions or tuples.
It would just be good if there was a standard and universally recognizable way to do it. A standard abstract class for message-sending that you can subclass. With standard subclasses for std-out, std-err, std-in, file-out and file-in. If you really don't want to use an operator, then just some variadic functions?
 I didn't expect to convince you <g>.
Right back at you. :) But if you leave the streaming operator out of it, how about that class? I could of course write one myself, but it wouldn't be standardized. And I think it should be.
Feb 14 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Michiel wrote:
 This can be done using variadic functions or tuples.
It would just be good if there was a standard and universally recognizable way to do it. A standard abstract class for message-sending that you can subclass. With standard subclasses for std-out, std-err, std-in, file-out and file-in.
Have you looked at Tango's IO classes? I think Tango has that pretty much. --bb
Feb 14 2007
parent reply Michiel <nomail hotmail.com> writes:
 It would just be good if there was a standard and universally recognizable way
to
 do it.

 A standard abstract class for message-sending that you can subclass. With
standard
 subclasses for std-out, std-err, std-in, file-out and file-in.
Have you looked at Tango's IO classes? I think Tango has that pretty much.
I had actually not. :) I'll look into that, thanks. I still think it deserves to be in the standard library, though.
Feb 14 2007
parent BCS <BCS pathlink.com> writes:
Michiel wrote:
It would just be good if there was a standard and universally recognizable way
to
do it.

A standard abstract class for message-sending that you can subclass. With
standard
subclasses for std-out, std-err, std-in, file-out and file-in.
Have you looked at Tango's IO classes? I think Tango has that pretty much.
I had actually not. :) I'll look into that, thanks. I still think it deserves to be in the standard library, though.
that's what Tango is trying to be, *the* standard library.
Feb 14 2007
prev sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 14 Feb 2007 14:45:50 -0800, Walter Bright wrote:

 Michiel wrote:
 To sum up: I love D. But I also like the streaming operator.
I didn't expect to convince you <g>.
Moving away from the idea of *overloading* existing operators, is there any case for new operators that implement I/O? -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 15/02/2007 11:36:07 AM
Feb 14 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Wed, 14 Feb 2007 14:45:50 -0800, Walter Bright wrote:
 
 Michiel wrote:
 To sum up: I love D. But I also like the streaming operator.
I didn't expect to convince you <g>.
Moving away from the idea of *overloading* existing operators, is there any case for new operators that implement I/O?
Imho the only serious reason for overloading << in C++ for I/O was the lack of decent variadic functions. Andrei
Feb 14 2007
prev sibling next sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Walter Bright wrote
 You see some code:
 
      x = a + b;
 
 and what do you think? I think "add a and b, assign the result to
 x"
What one thinks depends on the education. As soon as one has exposed ones thinking to abstract algebras, the mathematical structures of groups, rings and fields, etc., one looses the binding of addition to "+". It comes back, when one is assured, that the operands are indeed the usual numbers. "x = a + b;" may then very well mean: start rocket squads from locations a and b, combine them("+") to an attack formation and let them attack("=") location x. Please be aware that also D currently breaks your thinking: x = b; can currently assign a value to b. This contradicts your statement above. -manfred
Feb 14 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Manfred Nowak wrote:
 Walter Bright wrote
 You see some code:

      x = a + b;

 and what do you think? I think "add a and b, assign the result to
 x"
What one thinks depends on the education.
Of course. I assume here a computer programming background, where the conventional meaning is, well, conventional. We can relate this to the Lisp thread here, where (1- x) means x-1. The unconventionality of the Lisp syntax makes it a hard sell.
 Please be aware that also D currently breaks your thinking:
   
   x = b;
 
 can currently assign a value to b. This contradicts your statement 
 above.
I disagree, because this is in the context of computer programming, not abstract algebra. C++ stands out alone in its use of << for such, and is therefore unconventional.
Feb 14 2007
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Walter Bright wrote

 I disagree, because this is in the context of computer
 programming, not abstract algebra. C++ stands out alone in its use
 of << for such, and is therefore unconventional.
I do not understand with what you disagree. However, I agree with you, that the semantics of << in C++ distorts at least my thinking as much as 1- x in the semantics of lisp. I understand your wish to have fixed meanings for several operators. But I do not understand that D does not provide similar operators for free usage. "+", "-" and all the others with fixed meaning could be prefixed for example by the degree symbol "°+", "°-" and provided with the same priorities as their unprefixed counter parts. Thus enabling a possibly interwoven parallel expression hierarchy. -manfred
Feb 14 2007
next sibling parent reply Michiel <nomail hotmail.com> writes:
 But I do not understand that D does not provide similar operators for
 free usage.
I would like that. Just some extra operators that you can use for your personalized types. I'm not sure if I like the prefix-idea, though. An operator should be intuitive (readable) and easy to use. A prefix pretty much removes that advantage, and I would rather use functions. (By the way, I think the operator-symbols should at least be on a standard keyboard.) But ->, <-, postfix !, !in, etc. would be good.
Feb 14 2007
parent Manfred Nowak <svv1999 hotmail.com> writes:
Michiel wrote

 An operator should
 be intuitive (readable) and easy to use. A prefix pretty much
 removes that advantage, and I would rather use functions. (By the
 way, I think the operator-symbols should at least be on a standard
 keyboard.) 
 
 But ->, <-, postfix !, !in, etc. would be good.
New operators are not intuitiv either, because one has to learn their priorities---or one will end up with fully parenthesed expressions. Whether a prefix or a postfix and what characters will be choosen are of minor importance. I referred to the degree sign, because it is on the german standard keyboard and reminds at the often used middle circle in abstract algebras. -manfred
Feb 14 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Manfred Nowak wrote:
 I understand your wish to have fixed meanings for several operators. 
 But I do not understand that D does not provide similar operators for 
 free usage.
 
 "+", "-" and all the others with fixed meaning could be prefixed for 
 example by the degree symbol "°+", "°-" and provided with the same 
 priorities as their unprefixed counter parts.
 
 Thus enabling a possibly interwoven parallel expression hierarchy.
The problem with using unicode operators is that unicode isn't as well supported as it should be. It's one thing to support unicode in the language, it's another to require one to use a unicode capable text editor to edit source code.
Feb 14 2007
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Walter Bright wrote

 The problem with using unicode operators is that unicode isn't as
 well supported as it should be. It's one thing to support unicode
 in the language, it's another to require one to use a unicode
 capable text editor to edit source code.
The degree sign does not require unicode; it is in the normal ISO/IEC 8859-1 or latin1 table. Every editor should be capable to display it. Every system should be able to enter it. The degree sign "°" is under windows reachable via <alt>0176. The masculine ordinal indicator "º" via <alt>0186. The currency sign "¤" via <alt>0164. So: there is no such problem. -manfred
Feb 14 2007
next sibling parent Derek Parnell <derek nomail.afraid.org> writes:
On Thu, 15 Feb 2007 01:53:47 +0000 (UTC), Manfred Nowak wrote:

 Walter Bright wrote
 
 The problem with using unicode operators is that unicode isn't as
 well supported as it should be. It's one thing to support unicode
 in the language, it's another to require one to use a unicode
 capable text editor to edit source code.
The degree sign does not require unicode; it is in the normal ISO/IEC 8859-1 or latin1 table. Every editor should be capable to display it. Every system should be able to enter it.
LOL ... just because something *should* doesn't mean that it *will*.
 The degree sign "°" is under windows reachable via <alt>0176.
 
 The masculine ordinal indicator "º" via <alt>0186.
 
 The currency sign "¤" via <alt>0164.
 
 So: there is no such problem.
The editors I use all trap keystrokes and none of them allow 'raw' alt-xxx entry of characters. So: there is such a problem. <G> -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 15/02/2007 1:15:44 PM
Feb 14 2007
prev sibling next sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Manfred Nowak wrote:
 The degree sign does not require unicode; it is in the normal ISO/IEC 
 8859-1 or latin1 table.
All characters above 0x7F require unicode multibyte encodings. The latin1 table is not an encoding subset of unicode.
Feb 14 2007
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Walter Bright wrote

 All characters above 0x7F require unicode multibyte encodings. The
 latin1 table is not an encoding subset of unicode.
This invalidates the proposed character only, which is of minor importance. -manfred
Feb 14 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Manfred Nowak wrote:
 Walter Bright wrote
 
 All characters above 0x7F require unicode multibyte encodings. The
 latin1 table is not an encoding subset of unicode.
This invalidates the proposed character only, which is of minor importance.
There isn't much left below 0x7F :-(
Feb 14 2007
parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
Walter Bright wrote:
 Manfred Nowak wrote:
 
 Walter Bright wrote

 All characters above 0x7F require unicode multibyte encodings. The
 latin1 table is not an encoding subset of unicode.
This invalidates the proposed character only, which is of minor importance.
There isn't much left below 0x7F :-(
Hmmmm, abstract algebra... would this be easily parsable? other objections? a = b (+) c; a = b (-) c; a = b (*) c; a = b (^) c; a = b (|) c; a = b ($) c; a = b (?) c; etc... That first one kinda reminds me of the everyone's favorite circle-with-plus-inside.
Feb 14 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Chad J wrote:
 Hmmmm, abstract algebra...
 would this be easily parsable?
 other objections?
 a = b (+) c;
 a = b (-) c;
 a = b (*) c;
 a = b (^) c;
 a = b (|) c;
 a = b ($) c;
 a = b (?) c;
 etc...
Yes.
Feb 14 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Chad J wrote:
 Hmmmm, abstract algebra...
 would this be easily parsable?
 other objections?
 a = b (+) c;
 a = b (-) c;
 a = b (*) c;
 a = b (^) c;
 a = b (|) c;
 a = b ($) c;
 a = b (?) c;
 etc...
Yes.
You mean yes on both counts? It's kind of a controversial thing, I know, but for some types there really are multiple ways to interpret basic operations. Matlab, for example, defines .* to mean element-wise multiplication and * to mean matrix multiplication. Both forms get plenty of use. (Same for exponentiation too -- .^ and ^ both exist). I don't really care either way, but there is at least one example of a widely used language, specifically for math, in which there are more than the standard set of operators. Also Ocaml or Haskell or one of those guys, I forget which, allows definition of new operators using any combination of characters used in the built-in operators. E.g. **+ or */* or +++ or +- can all be made into custom operators. I have to admit, /That/ one seems like a pretty bad idea to me. :-) --bb
Feb 14 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Chad J wrote:
 Hmmmm, abstract algebra...
 would this be easily parsable?
 other objections?
 a = b (+) c;
 a = b (-) c;
 a = b (*) c;
 a = b (^) c;
 a = b (|) c;
 a = b ($) c;
 a = b (?) c;
 etc...
Yes.
You mean yes on both counts? It's kind of a controversial thing, I know, but for some types there really are multiple ways to interpret basic operations. Matlab, for example, defines .* to mean element-wise multiplication and * to mean matrix multiplication. Both forms get plenty of use. (Same for exponentiation too -- .^ and ^ both exist). I don't really care either way, but there is at least one example of a widely used language, specifically for math, in which there are more than the standard set of operators. Also Ocaml or Haskell or one of those guys, I forget which, allows definition of new operators using any combination of characters used in the built-in operators. E.g. **+ or */* or +++ or +- can all be made into custom operators. I have to admit, /That/ one seems like a pretty bad idea to me. :-)
Cecil too. Didn't have a problem with it. http://compilers.iecc.com/comparch/article/96-12-127 Andrei
Feb 14 2007
prev sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Walter Bright wrote

 would this be easily parsable?
 other objections?
 a = b (+) c;
Yes.
Then this seems to be the way to go, thanks Chad. And because we are at it: please provide the ability to abstract operators into families of operators by allowing an index. This might look like (+:0), (+:1), (+:2), ... where (+) is a shorthand for (+:0). Because of the overloading capabilities I do not see any other urgent need for this than to allow redundancy, but I might be wrong. -manfred
Feb 15 2007
parent Manfred Nowak <svv1999 hotmail.com> writes:
Manfred Nowak wrote

 Because of the overloading capabilities I do not see any other
 urgent need for this than to allow redundancy, but I might be
 wrong. 
Addendum: Bill has given the example of normal and dotwise matrix operations. Because these cannot be expressed by overloading, there is a need for indexing. -manfred
Feb 15 2007
prev sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Manfred Nowak wrote:

 Walter Bright wrote
 
 The problem with using unicode operators is that unicode isn't as
 well supported as it should be. It's one thing to support unicode
 in the language, it's another to require one to use a unicode
 capable text editor to edit source code.
The degree sign does not require unicode; it is in the normal ISO/IEC 8859-1 or latin1 table. Every editor should be capable to display it. Every system should be able to enter it. The degree sign "°" is under windows reachable via <alt>0176. The masculine ordinal indicator "º" via <alt>0186. The currency sign "¤" via <alt>0164. So: there is no such problem. -manfred
Very good example of exactly why that would be a problem :) -- Lars Ivar Igesund blog at http://larsivi.net DSource & #D: larsivi Dancing the Tango
Feb 15 2007
parent Manfred Nowak <svv1999 hotmail.com> writes:
Lars Ivar Igesund wrote

 Very good example of exactly why that would be a problem :)
Thx. I simply should have edited a .d-file to avoid this stupid error. -manfred
Feb 15 2007
prev sibling next sibling parent Daniel Giddings <dgiddings bigworldtech.com> writes:
 3) Suppose you've got some relational operators and templates thrown in:
 
     a << b<c<3>> << c < d << e;
Won't that treat the >> in b<c<3>> as the shift right operator and fail compilation? a << b<c<3> > << c < d << e? which higlights how bad the syntax is I think. At least thats the only meaning of the code that makes sense to me ;-)
Feb 14 2007
prev sibling next sibling parent Alexander Panek <a.panek brainsware.org> writes:
Walter Bright wrote:
 [...]
 And to sum up:
 
     writefln(a, b, c, d);
 
 seems pretty intuitive to me. There's just no advantage to overloading <<.
I can just fully agree with you, though, I slightly like Tango's Cout better. But that's just me - used writefln back then, too. Frankly, using '>>' and '<<' for streams may seem appropriate, but it's just not any kind of arrow, or such. Bit shifting is a very good use for those operators, though maybe they should /actually/ be bit rotating..I always have to think of VHS cassettes and rewind/forward, when I see the operator. Best regards, Alex
Feb 14 2007
prev sibling next sibling parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
That's because you're a nerd, Walter <g>
I learned C++ I/O long long before I learned bit manipulation and bit 
shifting.

In fact, I was kinda surprised when I learned that C uses << as a bit 
shifting operator.

 Now back to '<<' and '>>'. To me, they are arithmetic operators, and 
 mean shift left and shift right. Shift is not an I/O operation. To say 
 they are intuitive is not because they are, but because C++ users are 
 simply accustomed to it.
Same goes for using them as bit shifting, to say it scream bit shifting is not because it does, but because you are accustomed to it. Walter Bright wrote:
 Michiel wrote:
  >> And we should keep on frowning on attempts to use overloaded << for I/O
  >> purposes <g>.
  >
  > Why? I think it's intuitive. The arrows point from the source of the 
 message to
  > the destination. Should the operator only be used for shifting 
 because that
  > happens to have been its first purpose?
  >
  > I also like how you can send a message to an abstract object. And 
 that can be a
  > cout, a cerr, an error console in a GUI, or something else. Same 
 thing the other
  > way around.
 
 Good question. C++ conventional wisdom is that's great, because it's 
 always been done that way in C++. Few question it.
 
 Let's step back a bit. Suppose I overload '+' to be "launch nuclear 
 missiles." You see some code:
 
     x = a + b;
 
 and what do you think? I think "add a and b, assign the result to x", 
 but instead, nuclear missiles are launched. It is completely unexpected 
 and unintuitive for '+' to mean anything other than 'add'. To do 
 something completely unrelated to 'add', one should instead write:
 
     x = launchNuclearMissiles(a, b);
 
 where the meaning is plain, or at least, one isn't assuming it's adding 
 a and b.
 
 So why overload operators at all? For the purposes of implementing UDTs 
 (User Defined Types) that have legitimate arithmetic operations on them, 
 such as matrices, extended precision numbers, etc.
 
 Now back to '<<' and '>>'. To me, they are arithmetic operators, and 
 mean shift left and shift right. Shift is not an I/O operation. To say 
 they are intuitive is not because they are, but because C++ users are 
 simply accustomed to it.
 
     a << b << c << d;
 
 That doesn't scream I/O to me. And if a, b, etc. are non-trivial 
 expressions, they wind up:
 
 1) interacting with the operator precedence of << in unfortunate ways
 
 2) suppose you want to print a variable shifted left by a few bits:
 
     a << (b << 3) << c << d;
 
 blech.
 
 3) Suppose you've got some relational operators and templates thrown in:
 
     a << b<c<3>> << c < d << e;
 
 and things just go downhill from there.
 
 4) Operators aren't very greppable, meaning it's hard to go grepping 
 through source code looking to see if << has been overloaded.
 
 5) The Spirit library for C++ uses operator overloading to create a 
 specialized DSL for specifying grammar. The trouble is, the code looks 
 exactly like regular C++ expressions (since new operators cannot be 
 defined, nor can operator precedence be changed), and there are some big 
 warning boxes that the resulting appearance may LOOK like C++ but is 
 about as far removed from it as one can get. This doesn't enhance 
 readability.
 
 And to sum up:
 
     writefln(a, b, c, d);
 
 seems pretty intuitive to me. There's just no advantage to overloading <<.
Feb 14 2007
parent Walter Bright <newshound digitalmars.com> writes:
Hasan Aljudy wrote:
 That's because you're a nerd, Walter <g>
Yup <g>.
 I learned C++ I/O long long before I learned bit manipulation and bit 
 shifting.
 
 In fact, I was kinda surprised when I learned that C uses << as a bit 
 shifting operator.
 
 Now back to '<<' and '>>'. To me, they are arithmetic operators, and 
 mean shift left and shift right. Shift is not an I/O operation. To say 
 they are intuitive is not because they are, but because C++ users are 
 simply accustomed to it.
Same goes for using them as bit shifting, to say it scream bit shifting is not because it does, but because you are accustomed to it.
Every other "C like" language uses << for shifting.
Feb 14 2007
prev sibling next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 Michiel wrote:
  >> And we should keep on frowning on attempts to use overloaded << for I/O
  >> purposes <g>.
  >
  > Why? I think it's intuitive. The arrows point from the source of the 
 message to
  > the destination. Should the operator only be used for shifting 
 because that
  > happens to have been its first purpose?
  >
  > I also like how you can send a message to an abstract object. And 
 that can be a
  > cout, a cerr, an error console in a GUI, or something else. Same 
 thing the other
  > way around.
 
 Good question. C++ conventional wisdom is that's great, because it's 
 always been done that way in C++. Few question it.
 
 Let's step back a bit. Suppose I overload '+' to be "launch nuclear 
 missiles." You see some code:
 
     x = a + b;
 
 and what do you think? I think "add a and b, assign the result to x", 
 but instead, nuclear missiles are launched. It is completely unexpected 
 and unintuitive for '+' to mean anything other than 'add'. To do 
 something completely unrelated to 'add', one should instead write:
 
     x = launchNuclearMissiles(a, b);
 
 where the meaning is plain, or at least, one isn't assuming it's adding 
 a and b.
 
 So why overload operators at all? For the purposes of implementing UDTs 
 (User Defined Types) that have legitimate arithmetic operations on them, 
 such as matrices, extended precision numbers, etc.
 
 Now back to '<<' and '>>'. To me, they are arithmetic operators, and 
 mean shift left and shift right. Shift is not an I/O operation. To say 
 they are intuitive is not because they are, but because C++ users are 
 simply accustomed to it.
 
     a << b << c << d;
 
 That doesn't scream I/O to me. 
I agree. There's another problem -- it really doesn't scale well. With printf, you can do "%3.5f" to print a floating point number with a given precision. With <<, that same operation is really horrible. I'd like to see a couple more clarifications in the spec, wrt operator overloading: * If both opAddAssign and opAdd are provided, the compiler is permitted to replace expressions of the form a = a+b; with a+=b; (that is, a+=b _must_ mean a=a+b). And similarly for the other operators. It's just insanity to make a+=b mean anything other that a = a + b. Incidentally, this would make my 'operator overloading without temporaries' proposal a non-breaking change. And if a, b, etc. are non-trivial
 expressions, they wind up:
 
 1) interacting with the operator precedence of << in unfortunate ways
 
 2) suppose you want to print a variable shifted left by a few bits:
 
     a << (b << 3) << c << d;
 
 blech.
 
 3) Suppose you've got some relational operators and templates thrown in:
 
     a << b<c<3>> << c < d << e;
 
 and things just go downhill from there.
 
 4) Operators aren't very greppable, meaning it's hard to go grepping 
 through source code looking to see if << has been overloaded.
 
 5) The Spirit library for C++ uses operator overloading to create a 
 specialized DSL for specifying grammar. The trouble is, the code looks 
 exactly like regular C++ expressions (since new operators cannot be 
 defined, nor can operator precedence be changed), and there are some big 
 warning boxes that the resulting appearance may LOOK like C++ but is 
 about as far removed from it as one can get. This doesn't enhance 
 readability.
 
 And to sum up:
 
     writefln(a, b, c, d);
 
 seems pretty intuitive to me. There's just no advantage to overloading <<.
Feb 14 2007
next sibling parent reply Michiel <nomail please.com> writes:
Don Clugston wrote:

 I agree. There's another problem -- it really doesn't scale well. With
 printf, you can do "%3.5f" to print a floating point number with a given
 precision. With <<, that same operation is really horrible.
But it has the added advantage that there is no need for run-time parsing of that string. C++ parses all << stuff at compile time. -- Michiel
Feb 15 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Michiel wrote:
 Don Clugston wrote:
 
 I agree. There's another problem -- it really doesn't scale well. With
 printf, you can do "%3.5f" to print a floating point number with a given
 precision. With <<, that same operation is really horrible.
But it has the added advantage that there is no need for run-time parsing of that string. C++ parses all << stuff at compile time.
I wonder how much difference that makes given that most IO tends to be slow anyway. On a 1 GHz proc, with a disk that has 10msec seek time that means a hit to the disk can cost you ten million cycles, right? Spending a few cycles to parse a format string at runtime isn't going to kill you. --bb
Feb 15 2007
parent reply Michiel <nomail please.com> writes:
Bill Baxter wrote:

 But it has the added advantage that there is no need for run-time
 parsing of that string. C++ parses all << stuff at compile time.
I wonder how much difference that makes given that most IO tends to be slow anyway. On a 1 GHz proc, with a disk that has 10msec seek time that means a hit to the disk can cost you ten million cycles, right? Spending a few cycles to parse a format string at runtime isn't going to kill you.
Well, you're right of course. But strictly speaking it's still an advantage. ;) -- Michiel
Feb 15 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Michiel wrote:
 Bill Baxter wrote:
 
 But it has the added advantage that there is no need for run-time
 parsing of that string. C++ parses all << stuff at compile time.
I wonder how much difference that makes given that most IO tends to be slow anyway. On a 1 GHz proc, with a disk that has 10msec seek time that means a hit to the disk can cost you ten million cycles, right? Spending a few cycles to parse a format string at runtime isn't going to kill you.
Well, you're right of course. But strictly speaking it's still an advantage. ;)
C++ iostreams has the further "advantage" of being neither exception-safe nor thread-safe (since it manipulates global state in order to do formatting). This is only excusable because iostreams was added to C++ years before exception handling was and before anyone knew anything about threaded programming.
Feb 15 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Michiel wrote:
 Bill Baxter wrote:

 But it has the added advantage that there is no need for run-time
 parsing of that string. C++ parses all << stuff at compile time.
I wonder how much difference that makes given that most IO tends to be slow anyway. On a 1 GHz proc, with a disk that has 10msec seek time that means a hit to the disk can cost you ten million cycles, right? Spending a few cycles to parse a format string at runtime isn't going to kill you.
Well, you're right of course. But strictly speaking it's still an advantage. ;)
C++ iostreams has the further "advantage" of being neither exception-safe nor thread-safe (since it manipulates global state in order to do formatting). This is only excusable because iostreams was added to C++ years before exception handling was and before anyone knew anything about threaded programming.
It's a myth that iostreams benefit much the speed advantage of not needing to do type retrieval at runtime. They are marred by a convoluted design and the need to synchronize with C's stdio library. By the time you saved that penny, you've spent all of those pounds already. Andrei
Feb 15 2007
parent James Dennett <jdennett acm.org> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Walter Bright wrote:
 Michiel wrote:
 Bill Baxter wrote:

 But it has the added advantage that there is no need for run-time
 parsing of that string. C++ parses all << stuff at compile time.
I wonder how much difference that makes given that most IO tends to be slow anyway. On a 1 GHz proc, with a disk that has 10msec seek time that means a hit to the disk can cost you ten million cycles, right? Spending a few cycles to parse a format string at runtime isn't going to kill you.
Well, you're right of course. But strictly speaking it's still an advantage. ;)
C++ iostreams has the further "advantage" of being neither exception-safe nor thread-safe (since it manipulates global state in order to do formatting).
Hmmmmn, the only global state is the standard stream objects, and you don't have to use those. The use of globals is certainly a nightmare for thread-safety (though exception safety is less of an issue), but it's limited to this one set of global variables.
 This is only excusable because iostreams was
 added to C++ years before exception handling was and before anyone
 knew anything about threaded programming.
It's a myth that iostreams benefit much the speed advantage of not needing to do type retrieval at runtime.
I think current implementations aren't very good. And given how long we've had since IOStreams was first used, that's not an optimistic picture.
 They are marred by a convoluted
 design and the need to synchronize with C's stdio library.
The latter is turned off with a function call, which does make a significant difference. The convoluted design is just unfortunate. IOStreams isn't useful for much serious work. Not for notational reasons though; the design is just lousy. -- James
Feb 17 2007
prev sibling parent reply renoX <renosky free.fr> writes:
Don Clugston a écrit :
 I agree. There's another problem -- it(<<) really doesn't scale well. With 
 printf, you can do "%3.5f" to print a floating point number with a given 
 precision. With <<, that same operation is really horrible.
Agreed, and writef is even better: writef("x is %d",vx," y is %g\n",vy); No need to put the format string on one side and variables on the other side.. It has still deficiencies: - a syntax 'a la Ruby' would permit to see better the resulting string: writef("x is %{vx}, y is %{vy}\n"); of course you could still specify the format if you want: writef("x is %08x{vx}\n"); - why writef doesn't call to_String on struct or on a type? I.e: typedef int foo; char[] toString(foo x) { return "foo"; } doesn't work as expected in a writef, same with a struct defining a toString function.. renoX
Feb 15 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
renoX wrote:
 - a syntax 'a la Ruby' would permit to see better the resulting string:
 writef("x is %{vx}, y is %{vy}\n"); of course you could still specify 
 the format if you want: writef("x is %08x{vx}\n");
A syntax like this may be possible with string mixins. I'm pretty sure you could write that like this: --- mixin(write!("x is %{vx}, y is %{vy}\n")); --- and get away with it, given the proper 'write' template. It might not even be very difficult, it'd just need to find the %{} groups, parse them out and generate the proper call(s) to other functions. The backend could be writef, or Tango's Stdout.format or something custom-written.
Feb 15 2007
next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 renoX wrote:
 - a syntax 'a la Ruby' would permit to see better the resulting string:
 writef("x is %{vx}, y is %{vy}\n"); of course you could still specify 
 the format if you want: writef("x is %08x{vx}\n");
A syntax like this may be possible with string mixins. I'm pretty sure you could write that like this: --- mixin(write!("x is %{vx}, y is %{vy}\n")); --- and get away with it, given the proper 'write' template. It might not even be very difficult, it'd just need to find the %{} groups, parse them out and generate the proper call(s) to other functions. The backend could be writef, or Tango's Stdout.format or something custom-written.
Walter was talking about implementing this as an example :o). Andrei
Feb 15 2007
prev sibling parent renoX <renosky free.fr> writes:
Frits van Bommel a écrit :
 renoX wrote:
 - a syntax 'a la Ruby' would permit to see better the resulting string:
 writef("x is %{vx}, y is %{vy}\n"); of course you could still specify 
 the format if you want: writef("x is %08x{vx}\n");
A syntax like this may be possible with string mixins. I'm pretty sure you could write that like this: --- mixin(write!("x is %{vx}, y is %{vy}\n")); --- and get away with it, given the proper 'write' template. It might not even be very difficult, it'd just need to find the %{} groups, parse them out and generate the proper call(s) to other functions. The backend could be writef, or Tango's Stdout.format or something custom-written.
I know, that's in my TODO list, but I wanted to do 'printable enums' first and I had 'startup' problem (not being very familiar with template programming). Now that kevin bealer has provided 'reflexive enums', I want to refine the implementation (struct or not struct?), do the write stuff, then world domination. renoX
Feb 16 2007
prev sibling parent nazo <lovesyao gmail.com> writes:
I didn't see all posts. but "stdout ~= tuple(a,b,c,d)" is best for 
Output, I think. and need more short tuple syntax like a:b:c:d.

Example:
stdout ~= a:b:c:d; //call stdout.opCat(a,b,c,d)
stdout ~= e:f;
stdout ~= g:h;

I have no idea for Input best solution without opPopAssign_r.

Example:
int a; char[] b;
a:b = stdin.pop; //call stdin.opPopAssign_r!(typeof(a:b))();
                  //return type is typeof(a:b).
                  //"decide template from return type" is needed.

These ideas have problem for binary size :-(
#Sorry for my poor English
Feb 15 2007