Bug 4660 – Different writeln output for lazy sequences
Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2010-08-17T05:55:00Z
Last change time
2014-08-15T17:54:52Z
Assigned to
nobody
Creator
bearophile_hugs
Comments
Comment #0 by bearophile_hugs — 2010-08-17T05:55:42Z
This D2 program uses writeln to print a lazy sequence of integers:
import std.stdio: writeln;
import std.algorithm: map;
void main() {
auto r = map!q{a+1}([1, 2, 3]);
writeln(r);
}
Output with dmd 2.048:
[2, 3, 4]
But when I print a lazy sequence I'd like some cue in the textual output that the data being printed isn't an array.
In theory the delimiters can just be omitted for lazy sequences, but then if you print a collection of lazy sequences you can't where one sequence ends and the successive ends. So it's better to use delimiters, but different ones.
So this is a possible better output:
(2, 3, 4)
Comment #1 by bearophile_hugs — 2011-01-09T03:00:22Z
Now I think that a better output is:
[2; 3; 4]
Comment #2 by hsteoh — 2014-08-09T14:52:06Z
Current workaround:
writefln("[%(%s; %)]", r);
Is this acceptable?
Comment #3 by bearophile_hugs — 2014-08-14T18:36:31Z
(In reply to hsteoh from comment #2)
> Current workaround:
>
> writefln("[%(%s; %)]", r);
>
> Is this acceptable?
It's a simple workaround, but it misses my point: I have suggested to print lazy sequences using ";" as items separator to help the programmer tell apart lazy sequences from arrays (and array-like) data structures. So the purpose of using ";" is to give more information to the programmer that sees a textual output. If you use "[%(%s; %)]" to format a lazy range, you already know that you are dealing with a lazy range, so the textual output is not giving you much new information. So I think if we want to change this, it has to be a built-in feature of writeln. Otherwise we can close down this ER as wontfix, because it's not terribly important.
Comment #4 by k.hara.pg — 2014-08-15T11:14:05Z
writeln formats all ranges by using the style [a, b, ...]. And array is a range in D.
Additionally, writeln always evaluate all range elements to print them. So, the range laziness is meaningless when you want to print it. So they are printed by using same style by default.
If you want the semicolon-separated style, use the format specifiers "%( ... %| ... %)".
Comment #5 by andrei — 2014-08-15T15:00:01Z
I agree with Kenji.
Comment #6 by hsteoh — 2014-08-15T15:05:57Z
I agree with Kenji too.
Comment #7 by bearophile_hugs — 2014-08-15T17:31:17Z
(In reply to Kenji Hara from comment #4)
> writeln formats all ranges by using the style [a, b, ...].
That's the default I'd like to change.
> And array is a range in D.
Also a string is a range, but it gets a different output, it doesn't look like an array of chars.
For the programmer a lazy range and an eager one are quite different in D (in Haskell they are more transparent).
> Additionally, writeln always evaluate all range elements to print them.
But probably writeln doesn't collect all the items before printing them, it probably converts them one at a time (otherwise writeln needs an improvement). So the information of the eagerness or lazyness of the given range is still available inside writeln.
> If you want the semicolon-separated style, use the format specifiers "%( ...
> %| ... %)".
This is useless, as I explained above, because the point of the ";" is to give me information. There's little point in giving information, and even as a convention no one else will adopt it.
I think you are missing the point, but it doesn't matter much, because it's not very important. Closed issue as wontfix.
Comment #8 by hsteoh — 2014-08-15T17:54:52Z
The point is that writeln treats both lazy and non-lazy ranges generically, that's why it does not distinguish between them. If you really wanted to distinguish between them, you could do this:
-----
string getFmtString(R)(R range)
if (isInputRange!R)
{
// Here we assume eager == array
static if (is(R : A[], A))
return "[%(%s, %)]";
else
return "[%(%s; %)]";
}
auto myRange = ...;
writefln(myRange.getFmtString, myRange); // this will do what you want, generically
-----
Note that the above code is not 100% fool-proof; for example, R could be a wrapper struct around an array, so the static if wouldn't identify it as an eager range, but in fact it's actually eager. Or it could be a struct that generates data in blocks of 500 elements each time -- would that qualify as lazy or eager? Basically, there really isn't a 100% foolproof, generic way to determine if something is eager or not. In fact, I don't know of a definition of lazy vs. eager that covers 100% of the cases.