www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Printing tuples

reply bearophile <bearophileHUGS lycos.com> writes:
Textual printing in D (and other languages) has some needs like:

1) It needs to be readable, because first of all is meant to be read by people.

2) Where possible it needs to be as unambiguous as possible, because when you
have several writeln you want all the help to know what part of the text is
produced by one specific writeln. Often it's useful to receive some information
about the type of the printed data, because in D you are allowed to write:

void foo(T)(T x) {
    writeln(x);
}

In such cases it's useful to have some hint of the type of x in the textual
output itself.

3) It's often handy to have a textual output that is parsable to reproduce the
printed data structure (it's a kind of serialization).


But such needs sometimes go against each other. Serialization asks for a fully
unambiguous texual representation, but this sometimes produces a long or hard
to read textual output. In such cases you have to choose what purpose is more
important, or what's more important between consistency and practicality. I
think that readability usually wins here because the main purpose of writeln()
is not serialization. Serializability ("unprinting") is a secondary purpose of
writeln().


Both common proverbs and the Python Zen often say opposite things because
reality is complex, and hard strictly consistent rules in practice are often
suboptimal:

 Readability counts.
 Special cases aren't special enough to break the rules.
 Although practicality beats purity.
To show my point here is a small program, it generates just fifteen small tuples and prints them. I print tuples often in my code, so I am interested in this: import std.stdio, std.typecons; void main () { alias Tuple!(double,"posx", double,"posy", size_t,"count") T; T[] a; foreach (i; 0 .. 15) a ~= T(i*1.5, i*2.2, i); writeln(a[0]); writeln(a); } With the latest Phobos beta it prints: Tuple!(double,"posx",double,"posy",uint,"count")(0, 0, 0) [Tuple!(double,"posx",double,"posy",uint,"count")(0, 0, 0), Tuple!(double,"posx",double,"posy",uint,"count")(1.5, 2.2, 1), Tuple!(double,"posx",double,"posy",uint,"count")(3, 4.4, 2), Tuple!(double,"posx",double,"posy",uint,"count")(4.5, 6.6, 3), Tuple!(double,"posx",double,"posy",uint,"count")(6, 8.8, 4), Tuple!(double,"posx",double,"posy",uint,"count")(7.5, 11, 5), Tuple!(double,"posx",double,"posy",uint,"count")(9, 13.2, 6), Tuple!(double,"posx",double,"posy",uint,"count")(10.5, 15.4, 7), Tuple!(double,"posx",double,"posy",uint,"count")(12, 17.6, 8), Tuple!(double,"posx",double,"posy",uint,"count")(13.5, 19.8, 9), Tuple!(double,"posx",double,"posy",uint,"count")(15, 22, 10), Tuple!(double,"posx",double,"posy",uint,"count")(16.5, 24.2, 11), Tuple!(double,"posx",double,"posy",uint,"count")(18, 26.4, 12), Tuple!(double,"posx",double,"posy",uint,"count")(19.5, 28.6, 13), Tuple!(double,"posx",double,"posy",uint,"count")(21, 30.8, 14)] This textual output is good because it's a fully precise textual representation of the printed data structure, but in my opinion this is too much long and noisy. The information is almost drowning in noise. So: 1) I like that textual representation of single tuples (like the one printed by writeln(a[0]); ); 2) But I'd like a shorter textual representation of tuples when they are inside a collection. So I'd like this textual output for that program: Tuple!(double,"posx",double,"posy",uint,"count")(0, 0, 0) [tuple(0.0, 0.0, 0), tuple(1.5, 2.2, 1), tuple(3.0, 4.4, 2), tuple(4.5, 6.6, 3), tuple(6.0, 8.8, 4), tuple(7.5, 11.0, 5), tuple(9.0, 13.2, 6), tuple(10.5, 15.4, 7), tuple(12.0, 17.6, 8), tuple(13.5, 19.8, 9), tuple(15.0, 22.0, 10), tuple(16.5, 24.2, 11), tuple(18.0, 26.4, 12), tuple(19.5, 28.6, 13), tuple(21.0, 30.8, 14)] Notes: - This textual representation can be deserialized just like the precedent one, but it's more readable. - This textual representation is not exactly the same, because the field named are lost in the array. - Floating point values have a ".0" added where they don't have decimal digits, this allows all those tuples to be of the same type (double,double,int). This discussion comes from this bug report: http://d.puremagic.com/issues/show_bug.cgi?id=3813 Bye, bearophile
Sep 06 2011
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/06/2011 11:30 AM, bearophile wrote:
 Textual printing in D (and other languages) has some needs like:

 1) It needs to be readable, because first of all is meant to be read by people.

 2) Where possible it needs to be as unambiguous as possible, because when you
have several writeln you want all the help to know what part of the text is
produced by one specific writeln. Often it's useful to receive some information
about the type of the printed data, because in D you are allowed to write:

 void foo(T)(T x) {
      writeln(x);
 }

 In such cases it's useful to have some hint of the type of x in the textual
output itself.

 3) It's often handy to have a textual output that is parsable to reproduce the
printed data structure (it's a kind of serialization).


 But such needs sometimes go against each other. Serialization asks for a fully
unambiguous texual representation, but this sometimes produces a long or hard
to read textual output. In such cases you have to choose what purpose is more
important, or what's more important between consistency and practicality. I
think that readability usually wins here because the main purpose of writeln()
is not serialization. Serializability ("unprinting") is a secondary purpose of
writeln().


 Both common proverbs and the Python Zen often say opposite things because
reality is complex, and hard strictly consistent rules in practice are often
suboptimal:

 Readability counts.
 Special cases aren't special enough to break the rules.
 Although practicality beats purity.
To show my point here is a small program, it generates just fifteen small tuples and prints them. I print tuples often in my code, so I am interested in this: import std.stdio, std.typecons; void main () { alias Tuple!(double,"posx", double,"posy", size_t,"count") T; T[] a; foreach (i; 0 .. 15) a ~= T(i*1.5, i*2.2, i); writeln(a[0]); writeln(a); } With the latest Phobos beta it prints: Tuple!(double,"posx",double,"posy",uint,"count")(0, 0, 0) [Tuple!(double,"posx",double,"posy",uint,"count")(0, 0, 0), Tuple!(double,"posx",double,"posy",uint,"count")(1.5, 2.2, 1), Tuple!(double,"posx",double,"posy",uint,"count")(3, 4.4, 2), Tuple!(double,"posx",double,"posy",uint,"count")(4.5, 6.6, 3), Tuple!(double,"posx",double,"posy",uint,"count")(6, 8.8, 4), Tuple!(double,"posx",double,"posy",uint,"count")(7.5, 11, 5), Tuple!(double,"posx",double,"posy",uint,"count")(9, 13.2, 6), Tuple!(double,"posx",double,"posy",uint,"count")(10.5, 15.4, 7), Tuple!(double,"posx",double,"posy",uint,"count")(12, 17.6, 8), Tuple!(double,"posx",double,"posy",uint,"count")(13.5, 19.8, 9), Tuple!(double,"posx",double,"posy",uint,"count")(15, 22, 10), Tuple!(double,"posx",double,"posy",uint,"count")(16.5, 24.2, 11), Tuple!(double,"posx",double,"posy",uint,"count")(18, 26.4, 12), Tuple!(double,"posx",double,"posy",uint,"count")(19.5, 28.6, 13), Tuple!(double,"posx",double,"posy",uint,"count")(21, 30.8, 14)] This textual output is good because it's a fully precise textual representation of the printed data structure, but in my opinion this is too much long and noisy. The information is almost drowning in noise. So: 1) I like that textual representation of single tuples (like the one printed by writeln(a[0]); ); 2) But I'd like a shorter textual representation of tuples when they are inside a collection. So I'd like this textual output for that program: Tuple!(double,"posx",double,"posy",uint,"count")(0, 0, 0) [tuple(0.0, 0.0, 0), tuple(1.5, 2.2, 1), tuple(3.0, 4.4, 2), tuple(4.5, 6.6, 3), tuple(6.0, 8.8, 4), tuple(7.5, 11.0, 5), tuple(9.0, 13.2, 6), tuple(10.5, 15.4, 7), tuple(12.0, 17.6, 8), tuple(13.5, 19.8, 9), tuple(15.0, 22.0, 10), tuple(16.5, 24.2, 11), tuple(18.0, 26.4, 12), tuple(19.5, 28.6, 13), tuple(21.0, 30.8, 14)] Notes: - This textual representation can be deserialized just like the precedent one, but it's more readable. - This textual representation is not exactly the same, because the field named are lost in the array. - Floating point values have a ".0" added where they don't have decimal digits, this allows all those tuples to be of the same type (double,double,int). This discussion comes from this bug report: http://d.puremagic.com/issues/show_bug.cgi?id=3813 Bye, bearophile
Note that TypeTuple!(1,2,3).stringof == "tuple(1,2,3)" Which is bad imho.
Sep 06 2011
prev sibling parent reply travert phare.normalesup.org (Christophe) writes:
writeln never writes the name of the printed variable, except for a 
tuple (or some custom types,obviously). Moreover, the output of 
writeln(T) and writeln(T[]) should be consistent.

The output should look somthing like: "(1.5, 1, my string)".

And if you want to know the name of the tuple before your tuple, you 
should write:
writeln(typeof(t).stringof, t)

Do you agree ?

-- 
Christophe
Sep 06 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
Christophe:

 Moreover, the output of 
 writeln(T) and writeln(T[]) should be consistent.
Years after my first request, now given this program: import std.stdio; void main() { writeln("foo"); writeln(["foo"]); } D prints (this problem is what has pushed me to add bug reports to Bugzilla in the first place, so it was one of my pet peeves): foo ["foo"]
 The output should look somthing like: "(1.5, 1, my string)".
This is very clean, but this is different from the look of D tuple literals. Bye, bearophile
Sep 06 2011
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/6/11 12:32 PM, bearophile wrote:
 Christophe:

 Moreover, the output of
 writeln(T) and writeln(T[]) should be consistent.
Years after my first request, now given this program: import std.stdio; void main() { writeln("foo"); writeln(["foo"]); } D prints (this problem is what has pushed me to add bug reports to Bugzilla in the first place, so it was one of my pet peeves):
Yah, the problem with making array elements printed the same as individual items is ambiguity. Inside an array the reader (human or not) must unambiguously identify elements from separators, otherwise printing square brackets and commas results in confusion. This reminds me, we should have a function e.g. std.conv.escape() that given the string "foo\nbar" returns the string "\"foo\\nbar\"". I mean sometimes we want to print "DSON"-formatted stuff without it being in an array. Andrei
Sep 06 2011
parent Alix Pexton <alix.DOT.pexton gmail.DOT.com> writes:
On 06/09/2011 19:24, Andrei Alexandrescu wrote:
 sometimes we want to print "DSON"-formatted stuff without it being in an
 array.

 Andrei
If JSON stands for JavaScript Object Notation[1], then the D equivalent would surely be DON? I'm sure no prominent members of the community will mind and there would be absolutely no confusion ^^ A... [1] see json.org, although I acknowledge that some people prefer to insert "Serialized".
Sep 06 2011