April 23, 2009
Daniel Keep wrote:
> 
> Andrei Alexandrescu wrote:
>> Steven Schveighoffer wrote:
>>> On Thu, 23 Apr 2009 09:24:59 -0400, Andrei Alexandrescu
>>> <SeeWebsiteForEmail@erdani.org> wrote:
>>>
>>>> Steven Schveighoffer wrote:
>>>>> This has to go into object.d and be part of the runtime, where
>>>>> std.range doesn't exist.  There is nothing stopping you from calling:
>>>>>  streamOut(&outputrange.put);
>>>>>  So I'd rather have a sink function.
>>>> It must be a sink _object_ so it can hold its own state. And it must
>>>> support put() so it integrates with statically-bound output ranges.
>>>>
>>>> interface OutRange
>>>> {
>>>>      void put(... a number of overloads ...);
>>>> }
>>> I see now, yes I agree (I think that was don's original request
>>> anyways).  That interface has to go in the runtime, though.
>>>
>>> We may not be able to do this using templates... it has to be a
>>> virtual function in Object to be on-par with toString.  This means
>>> struct interfaces are a requirement if you want to use ranges :(
>> We're in good shape actually. OutRange as a dynamic interface and an
>> implicit interface using .put against a struct will work just as well
>> with templates. (The template doesn't care whether obj.put(x) is a
>> virtual call or statically-bound call.)
>>
>> Andrei
> 
> "We may not be able to do this using templates... it has to be a virtual
> function in Object to be on-par with toString."
> 
> Note that toString is a virtual method.  You are proposing replacing
> toString with a template.  You cannot have virtual template methods.
> Ergo, "new toString" would be inaccessible without the actual type, and
> certainly not at runtime.

The toStream that I have in mind is virtual and takes an interface of type OutRange as outlined above.

Andrei
April 23, 2009
Andrei Alexandrescu wrote:
> grauzone wrote:
>> Andrei Alexandrescu wrote:
>>> grauzone wrote:
>>>>> Yes. The way it should be is not with sink, but with the standard output iterator method put().
>>>>>
>>>>> void streamOut(T, R)(T object, R range)
>>>>> {
>>>>>     foreach(x; a) range.put(x);
>>>>>     range.put(b);
>>>>>     range.put(c);
>>>>> }
>>>>
>>>> Eh. Is a sink callback too simple and easy to use or what?
>>>
>>> ?
>>
>> Why make it more complicated than it has to be?
> 
> I am making it simpler.

How is it simpler?

sink(): simple delegate with the signature void delegate(char[] data);
output range: um what...? yeah, I know it has a put() method that takes... something. What exactly is it supposed to take in your example? Is streamOut() a method of the object to be dumped? What exact types to T and R have? (You need the exact type if streamOut is supposed to be a member function, and thus has to be virtual, so that you can it use like the Object.toString method.)

>> Also, I don't know ranges, but your example doesn't seem to make much sense.
> 
> s/but/consequently/

Probably. Just imagine you had to explain it to someone who's new to D. Actually, I _am_ new to D2.0.

So, um... what is a b c and T object?

> 
> Andrei
April 23, 2009
Georg Wrede wrote:
> Don wrote:
>> Georg Wrede wrote:
>>> Don wrote:
>>>> bearophile wrote:
>>>>> This post is mostly for Andrei.
>>>>> I have played with D2 a bit; probably I'll need months to digest it and its new Phobos2. While I explore Phobos I'll probably post some comments/bugs around here.
>>>>>
>>>>> After reading this:
>>>>> http://blogs.msdn.com/vcblog/archive/2009/04/22/decltype-c-0x-features-in-vc10-part-3.aspx 
>>>>>
>>>>> I have tried to write a toy implementation of it in D2 (not using Phobos2 yet):
>>>>>
>>>>> import std.stdio: writeln;
>>>>> import std.string: format;
>>>>>
>>>>> struct Watts {
>>> ...
>>>
>>>>> Two things to improve:
>>>>> 1) All structs must have a default built-in opString, a good representation can be:
>>>>> StructName(field_value1, field_value2, field_value1, ...).
>>>>> It's not a perfect textual representation, but it's WAY better than the current one (currently it shows just the struct name).
>>>>> (Printing the module name before the struct name is bad, most times is just noise)
>>>>
>>>> No!
>>>> <rant>
>>>> toString() is one of the most dreadful features in D. Trying to slightly improve it is a waste of time -- the whole concept needs to be redone.
>>>> It's horribly inflexible, tedious, and hugely inefficient. What more could there be to hate?
>>>>
>>>> - the object being called has no context. It doesn't know what format is desired, for example.
>>>> - you can't emulate formatting of built-in types, NOT EVEN int! You can't do left-align, pad with zeros, include + sign, display in hex.
>>>>
>>>> - it's got no stream concept. Every object has to create and manage its own buffer, and nobody knows if anyone actually needs it.
>>>>
>>>> It ought to be at least as simple as:
>>>>
>>>> struct Foo(A, B, C){
>>>> A[10] a;
>>>> B b;
>>>> C c;
>>>> void toString(Sink sink){
>>>>    foreach(x; a) sink(x);
>>>>    sink(b);
>>>>    sink(c);
>>>> }
>>>> }
>>>> ... but it's not, you have to create a silly buffer to put all your strings in, even if there are 200 million of them and your giant string is just going to be written to a file anyway.
>>>>
>>>> I'd like to see version(debug) {} put around Object.toString(). It's a deathtrap feature that's got no business being used other than for debugging.
>>>> </rant>
>>>
>>> First of all, printing stuff "struct.toString()" style is for two things:
>>>
>>>  o  Debugging
>>>  o  Small throwaway code snippets
>>>
>>> The latter mainly being for two purposes:
>>>
>>>  o  Testing quick concepts, trying out library functions, etc.
>>>  o  For the newbie, when he's learning D, but not output formatting.
>>>
>>> No "Real Program" uses this, because there you typically do proper formatting of the output anyway, and almost never print entire structs or objects as such. Instead, rather the information that they represent.
>>
>> How about something like BigInt? Why can't you just print it out?
> 
> ?? Why couldn't you?
> 
> They're not stored as strings (not Janice's anyway), but I don't understand the question.

You can write:

int a, b;
a=10; b=20;
writefln("%d %x", a, b);

I'd like to be able to write:

BigInt a, b;
a=10; b=20;
writefln("%d %x", a, b);

and have it behave exactly the same.


> 
>> BTW to everyone, 'Sink' was not a proposal. I was just saying that almost anything's better than the current toString().
April 23, 2009
Don wrote:
> Georg Wrede wrote:
>> Don wrote:
>>> Georg Wrede wrote:
>>>> Don wrote:
>>>>> bearophile wrote:
>>>>>> This post is mostly for Andrei.
>>>>>> I have played with D2 a bit; probably I'll need months to digest it and its new Phobos2. While I explore Phobos I'll probably post some comments/bugs around here.
>>>>>>
>>>>>> After reading this:
>>>>>> http://blogs.msdn.com/vcblog/archive/2009/04/22/decltype-c-0x-features-in-vc10-part-3.aspx 
>>>>>>
>>>>>> I have tried to write a toy implementation of it in D2 (not using Phobos2 yet):
>>>>>>
>>>>>> import std.stdio: writeln;
>>>>>> import std.string: format;
>>>>>>
>>>>>> struct Watts {
>>>> ...
>>>>
>>>>>> Two things to improve:
>>>>>> 1) All structs must have a default built-in opString, a good representation can be:
>>>>>> StructName(field_value1, field_value2, field_value1, ...).
>>>>>> It's not a perfect textual representation, but it's WAY better than the current one (currently it shows just the struct name).
>>>>>> (Printing the module name before the struct name is bad, most times is just noise)
>>>>>
>>>>> No!
>>>>> <rant>
>>>>> toString() is one of the most dreadful features in D. Trying to slightly improve it is a waste of time -- the whole concept needs to be redone.
>>>>> It's horribly inflexible, tedious, and hugely inefficient. What more could there be to hate?
>>>>>
>>>>> - the object being called has no context. It doesn't know what format is desired, for example.
>>>>> - you can't emulate formatting of built-in types, NOT EVEN int! You can't do left-align, pad with zeros, include + sign, display in hex.
>>>>>
>>>>> - it's got no stream concept. Every object has to create and manage its own buffer, and nobody knows if anyone actually needs it.
>>>>>
>>>>> It ought to be at least as simple as:
>>>>>
>>>>> struct Foo(A, B, C){
>>>>> A[10] a;
>>>>> B b;
>>>>> C c;
>>>>> void toString(Sink sink){
>>>>>    foreach(x; a) sink(x);
>>>>>    sink(b);
>>>>>    sink(c);
>>>>> }
>>>>> }
>>>>> ... but it's not, you have to create a silly buffer to put all your strings in, even if there are 200 million of them and your giant string is just going to be written to a file anyway.
>>>>>
>>>>> I'd like to see version(debug) {} put around Object.toString(). It's a deathtrap feature that's got no business being used other than for debugging.
>>>>> </rant>
>>>>
>>>> First of all, printing stuff "struct.toString()" style is for two things:
>>>>
>>>>  o  Debugging
>>>>  o  Small throwaway code snippets
>>>>
>>>> The latter mainly being for two purposes:
>>>>
>>>>  o  Testing quick concepts, trying out library functions, etc.
>>>>  o  For the newbie, when he's learning D, but not output formatting.
>>>>
>>>> No "Real Program" uses this, because there you typically do proper formatting of the output anyway, and almost never print entire structs or objects as such. Instead, rather the information that they represent.
>>>
>>> How about something like BigInt? Why can't you just print it out?
>>
>> ?? Why couldn't you?
>>
>> They're not stored as strings (not Janice's anyway), but I don't understand the question.
> 
> You can write:
> 
> int a, b;
> a=10; b=20;
> writefln("%d %x", a, b);
> 
> I'd like to be able to write:
> 
> BigInt a, b;
> a=10; b=20;
> writefln("%d %x", a, b);
> 
> and have it behave exactly the same.


Hmm. My idea was to only have writeln (and not writefln) be automated. (For the small/debugging purpose.)

Andrei seems to be at this, but right now I don't know enough details to say anything. It seems to be an even bigger thing than what I suggested, and knowing he does things in a universal way, one would assume that if a class "wants" to be printed in some way, then maybe there'll be some provisions for it.

But, things like BigInt, that really are classes or structs that have to be printed in a specific way, I have a hard time figuring out how to printe them using write_f_ln.

One way would be to have the format specification (as in "%x") be somehow passed to the toString of the struct/class. Then the class could decide for itself how to be printed in this case.


But even this is stretching it a bit, since some more complicated class/struct might need a more elaborate hint than just one letter. And by that time the whole thing starts to crumble, in usability issues, at least.

One thing we sholuld be wary of is overdesign. BigInt is ok, but by the time we try to include a class called CompleteSimulationOfAnF1RaceCar, we're screwed. :-)  I see no way to incorporate them into writefln or even plain writeln. Or at least, no *use*.


April 23, 2009
Georg Wrede wrote:
> Don wrote:
>> Georg Wrede wrote:
>>> Don wrote:
>>>> Georg Wrede wrote:
>>>>> Don wrote:
>>>>>> bearophile wrote:
>>>>>>> This post is mostly for Andrei.
>>>>>>> I have played with D2 a bit; probably I'll need months to digest it and its new Phobos2. While I explore Phobos I'll probably post some comments/bugs around here.
>>>>>>>
>>>>>>> After reading this:
>>>>>>> http://blogs.msdn.com/vcblog/archive/2009/04/22/decltype-c-0x-features-in-vc10-part-3.aspx 
>>>>>>>
>>>>>>> I have tried to write a toy implementation of it in D2 (not using Phobos2 yet):
>>>>>>>
>>>>>>> import std.stdio: writeln;
>>>>>>> import std.string: format;
>>>>>>>
>>>>>>> struct Watts {
>>>>> ...
>>>>>
>>>>>>> Two things to improve:
>>>>>>> 1) All structs must have a default built-in opString, a good representation can be:
>>>>>>> StructName(field_value1, field_value2, field_value1, ...).
>>>>>>> It's not a perfect textual representation, but it's WAY better than the current one (currently it shows just the struct name).
>>>>>>> (Printing the module name before the struct name is bad, most times is just noise)
>>>>>>
>>>>>> No!
>>>>>> <rant>
>>>>>> toString() is one of the most dreadful features in D. Trying to slightly improve it is a waste of time -- the whole concept needs to be redone.
>>>>>> It's horribly inflexible, tedious, and hugely inefficient. What more could there be to hate?
>>>>>>
>>>>>> - the object being called has no context. It doesn't know what format is desired, for example.
>>>>>> - you can't emulate formatting of built-in types, NOT EVEN int! You can't do left-align, pad with zeros, include + sign, display in hex.
>>>>>>
>>>>>> - it's got no stream concept. Every object has to create and manage its own buffer, and nobody knows if anyone actually needs it.
>>>>>>
>>>>>> It ought to be at least as simple as:
>>>>>>
>>>>>> struct Foo(A, B, C){
>>>>>> A[10] a;
>>>>>> B b;
>>>>>> C c;
>>>>>> void toString(Sink sink){
>>>>>>    foreach(x; a) sink(x);
>>>>>>    sink(b);
>>>>>>    sink(c);
>>>>>> }
>>>>>> }
>>>>>> ... but it's not, you have to create a silly buffer to put all your strings in, even if there are 200 million of them and your giant string is just going to be written to a file anyway.
>>>>>>
>>>>>> I'd like to see version(debug) {} put around Object.toString(). It's a deathtrap feature that's got no business being used other than for debugging.
>>>>>> </rant>
>>>>>
>>>>> First of all, printing stuff "struct.toString()" style is for two things:
>>>>>
>>>>>  o  Debugging
>>>>>  o  Small throwaway code snippets
>>>>>
>>>>> The latter mainly being for two purposes:
>>>>>
>>>>>  o  Testing quick concepts, trying out library functions, etc.
>>>>>  o  For the newbie, when he's learning D, but not output formatting.
>>>>>
>>>>> No "Real Program" uses this, because there you typically do proper formatting of the output anyway, and almost never print entire structs or objects as such. Instead, rather the information that they represent.
>>>>
>>>> How about something like BigInt? Why can't you just print it out?
>>>
>>> ?? Why couldn't you?
>>>
>>> They're not stored as strings (not Janice's anyway), but I don't understand the question.
>>
>> You can write:
>>
>> int a, b;
>> a=10; b=20;
>> writefln("%d %x", a, b);
>>
>> I'd like to be able to write:
>>
>> BigInt a, b;
>> a=10; b=20;
>> writefln("%d %x", a, b);
>>
>> and have it behave exactly the same.
> 
> 
> Hmm. My idea was to only have writeln (and not writefln) be automated. (For the small/debugging purpose.)
> 
> Andrei seems to be at this, but right now I don't know enough details to say anything. It seems to be an even bigger thing than what I suggested, and knowing he does things in a universal way, one would assume that if a class "wants" to be printed in some way, then maybe there'll be some provisions for it.
> 
> But, things like BigInt, that really are classes or structs that have to be printed in a specific way, I have a hard time figuring out how to printe them using write_f_ln.
> 
> One way would be to have the format specification (as in "%x") be somehow passed to the toString of the struct/class. Then the class could decide for itself how to be printed in this case.
> 
> 
> But even this is stretching it a bit, since some more complicated class/struct might need a more elaborate hint than just one letter. And by that time the whole thing starts to crumble, in usability issues, at least.
> 
> One thing we sholuld be wary of is overdesign. BigInt is ok, but by the time we try to include a class called CompleteSimulationOfAnF1RaceCar, we're screwed. :-)  I see no way to incorporate them into writefln or even plain writeln. Or at least, no *use*.

I think it'd be reasonable to limit things to the options available for built-in types. Outside of that, custom formatting functions make a lot of sense. The problem is that toString() _looks_ like it emulates built-in formatting, but it only does '%s'. So it's really beguiling.

BTW, when passing the output to a sink, it should be possible to (say) format your members with '%x' format, but you can't do that by permanently altering sink: it should revert to its previous value once you've sunk your last member. (I think this C++ iostreams got this wrong).
April 23, 2009
On Thu, 23 Apr 2009 10:30:15 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Daniel Keep wrote:
>>  Andrei Alexandrescu wrote:
>>> Steven Schveighoffer wrote:
>>>> On Thu, 23 Apr 2009 09:24:59 -0400, Andrei Alexandrescu
>>>> <SeeWebsiteForEmail@erdani.org> wrote:
>>>>
>>>>> Steven Schveighoffer wrote:
>>>>>> This has to go into object.d and be part of the runtime, where
>>>>>> std.range doesn't exist.  There is nothing stopping you from calling:
>>>>>>  streamOut(&outputrange.put);
>>>>>>  So I'd rather have a sink function.
>>>>> It must be a sink _object_ so it can hold its own state. And it must
>>>>> support put() so it integrates with statically-bound output ranges.
>>>>>
>>>>> interface OutRange
>>>>> {
>>>>>      void put(... a number of overloads ...);
>>>>> }
>>>> I see now, yes I agree (I think that was don's original request
>>>> anyways).  That interface has to go in the runtime, though.
>>>>
>>>> We may not be able to do this using templates... it has to be a
>>>> virtual function in Object to be on-par with toString.  This means
>>>> struct interfaces are a requirement if you want to use ranges :(
>>> We're in good shape actually. OutRange as a dynamic interface and an
>>> implicit interface using .put against a struct will work just as well
>>> with templates. (The template doesn't care whether obj.put(x) is a
>>> virtual call or statically-bound call.)
>>>
>>> Andrei
>>  "We may not be able to do this using templates... it has to be a virtual
>> function in Object to be on-par with toString."
>>  Note that toString is a virtual method.  You are proposing replacing
>> toString with a template.  You cannot have virtual template methods.
>> Ergo, "new toString" would be inaccessible without the actual type, and
>> certainly not at runtime.
>
> The toStream that I have in mind is virtual and takes an interface of type OutRange as outlined above.

OK, that's fine, I was working off your original proposal:

void streamOut(R)(R range)
{
     foreach(x; a) range.put(x);
     range.put(b);
     range.put(c);
}

Of course, this then eliminates structs from being OutRange's without having struct interfaces.  As most ranges are structs (and rightfully so, who wants to call 3 virtual functions every loop!), they would have to be wrapped under the current compiler. Or am I missing something else?

-Steve
April 23, 2009
Don wrote:
> Georg Wrede wrote:
>> One thing we sholuld be wary of is overdesign. BigInt is ok, but by the time we try to include a class called CompleteSimulationOfAnF1RaceCar, we're screwed. :-)  I see no way to incorporate them into writefln or even plain writeln. Or at least, no *use*.
> 
> I think it'd be reasonable to limit things to the options available for built-in types. Outside of that, custom formatting functions make a lot of sense. The problem is that toString() _looks_ like it emulates built-in formatting, but it only does '%s'. So it's really beguiling.

Heh, one thought is, suppose we could have arbitrary format specifications in writef. Say, if we wrote %&lkjasdf; this string would be passed to toString. (It would of course be up to the class to error-check the string, writefln obviously cant (or shouldn't) do it.)

So, things are doable. But I'm really not looking forward to that kind of sports. Any elaborate printing or formatting should be handled outside writefln &co.

> BTW, when passing the output to a sink, it should be possible to (say) format your members with '%x' format, but you can't do that by permanently altering sink: it should revert to its previous value once you've sunk your last member. (I think this C++ iostreams got this wrong).

I guess they, too, thought of making it "easier" in the wrong place. Just because you can, doesn't mean you have to.

(OT: an excellent example of this It's Done Because We Noticed We Could stuff is in Firefox. When a picture is a link to another page, and you want to drag that to the tab area, the entire picture is dragged with the mouse. Now, how the hell am I supposed to hit the small tab area when the large picture covers half of my Firefox??

So now I have to learn to remember to grab bigger pictures near some edge. And I really can't see *any* valid benefit for having to drag the picture. I'd rather have it the old way, where the mouse pointer simply changes shape, so you know you're dragging. Damn, damn...)
April 23, 2009
Georg Wrede wrote:
> Don wrote:
>> Georg Wrede wrote:
>>> One thing we sholuld be wary of is overdesign. BigInt is ok, but by the time we try to include a class called CompleteSimulationOfAnF1RaceCar, we're screwed. :-)  I see no way to incorporate them into writefln or even plain writeln. Or at least, no *use*.
>>
>> I think it'd be reasonable to limit things to the options available for built-in types. Outside of that, custom formatting functions make a lot of sense. The problem is that toString() _looks_ like it emulates built-in formatting, but it only does '%s'. So it's really beguiling.
> 
> Heh, one thought is, suppose we could have arbitrary format specifications in writef. Say, if we wrote %&lkjasdf; this string would be passed to toString. (It would of course be up to the class to error-check the string, writefln obviously cant (or shouldn't) do it.)
> 
> So, things are doable. But I'm really not looking forward to that kind of sports. Any elaborate printing or formatting should be handled outside writefln &co.

Take a look at the formatting library for Plan 9.  It’s very much like printf & co., except it exposes enough of its internals to allow for new formats to be added in.  See the documentation (for the *nix-ported version) at <http://swtch.com/plan9port/man/man3/print.html> and <http://swtch.com/plan9port/man/man3/fmtinstall.html>. For example:

typedef
struct {
	double	r, i;
} Complex;
#pragma  varargck  type "X"  Complex	// compiler checks type-safety

int
Xfmt(Fmt *f)
{
	Complex c;
	c = va_arg(f−>args, Complex);
	return fmtprint(f, "(%g,%g)", c.r, c.i); // can use flags here
}

main(...)
{
	Complex x = (Complex){ 1.5, −2.3 };
	fmtinstall('X', Xfmt);
	print("x = %X\n", x);
}

With D’s type-safe variadic functions, this model can be made really powerful.

—Joel Salomon
April 23, 2009
On Thu, 23 Apr 2009 18:28:53 +0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Denis Koroskin wrote:
>> Sink is okay, but most my usages belong to one of the two scenarios: 1)
>> I need a string representation of an Object - how is Sink useful
>> here? I just want to call obj.toString() and get the result 2) I need
>> to print it to stdout, thus I call writeln/Stdout(obj); - Sink is of
>> no use here again.
>
> I hear you, but there are quite a few more considerations that apply.
>
> For one, making string the common format of all objects is pretty hamfisted. We need to integrate things like binary streams too. Second, using string append and composing with it is bound to be inefficient, and for multiple reasons. You can't just say that a reasonable way to print a matrix is to call toString against it and print the resulting string. With the temporary buffer passed-in or not, that's just a terrible design.
>
> But you need not fear. Converting to string will remain easy and simple,
>   it will only be a particular case of streaming objects out.
>
>
> Andrei

I'm fine with that, especially if entire Phobos will be consistent with it (e.g. std.string.format accepts sink object etc). There a few problems with it, though.

I like the "toStream" name but I believe it is a bit misleading - if toString() returns a string, then toStream() should return stream, too, not accept it.

But what about custom formatting? Previously I stated that current scheme doesn't allow them, but it turned out to be possible.

For example, you introduced custom array formatting:

%(s; ) -> array.toString("s; ", ...);

Similar approach may be used for other user-defined formatting options:

%s -> obj.toString("s");
%d -> obj.toString("d");
...
%(any_set_of_characters) -> obj.toString("any_set_of_characters"); // including inner %() etc


Example: given an array of arrays of ints, print them as follows: "[ [1, 2, 3, 4, ]; [5, 6, 7, 8, ]; [9, a, b, c, ]; ]"

int[][] arrayOfArraysOfInts;
writefln("[ %([%(x, )]; )]", arrayOfArraysOfInts);

Just to make it more clear:

[ %([%(x, )]; )] -> "[ " ~ arrayOfArraysOfInts.toString("[%(x, )]; ") ~ "]"
April 23, 2009
On Thu, 23 Apr 2009 19:34:38 +0400, Don <nospam@nospam.com> wrote:

> Georg Wrede wrote:
>> Don wrote:
>>> Georg Wrede wrote:
>>>> Don wrote:
>>>>> Georg Wrede wrote:
>>>>>> Don wrote:
>>>>>>> bearophile wrote:
>>>>>>>> This post is mostly for Andrei.
>>>>>>>> I have played with D2 a bit; probably I'll need months to digest
>>>>>>>> it and its new Phobos2. While I explore Phobos I'll probably post
>>>>>>>> some comments/bugs around here.
>>>>>>>>
>>>>>>>> After reading this:
>>>>>>>> http://blogs.msdn.com/vcblog/archive/2009/04/22/decltype-c-0x-features-in-vc10-part-3.aspx
>>>>>>>> I have tried to write a toy implementation of it in D2 (not using
>>>>>>>> Phobos2 yet):
>>>>>>>>
>>>>>>>> import std.stdio: writeln;
>>>>>>>> import std.string: format;
>>>>>>>>
>>>>>>>> struct Watts {
>>>>>> ...
>>>>>>
>>>>>>>> Two things to improve:
>>>>>>>> 1) All structs must have a default built-in opString, a good
>>>>>>>> representation can be:
>>>>>>>> StructName(field_value1, field_value2, field_value1, ...).
>>>>>>>> It's not a perfect textual representation, but it's WAY better
>>>>>>>> than the current one (currently it shows just the struct name).
>>>>>>>> (Printing the module name before the struct name is bad, most
>>>>>>>> times is just noise)
>>>>>>>
>>>>>>> No!
>>>>>>> <rant>
>>>>>>> toString() is one of the most dreadful features in D. Trying to
>>>>>>> slightly improve it is a waste of time -- the whole concept needs
>>>>>>> to be redone.
>>>>>>> It's horribly inflexible, tedious, and hugely inefficient. What
>>>>>>> more could there be to hate?
>>>>>>>
>>>>>>> - the object being called has no context. It doesn't know what
>>>>>>> format is desired, for example.
>>>>>>> - you can't emulate formatting of built-in types, NOT EVEN int!
>>>>>>> You can't do left-align, pad with zeros, include + sign, display
>>>>>>> in hex.
>>>>>>>
>>>>>>> - it's got no stream concept. Every object has to create and manage its own buffer, and nobody knows if anyone actually needs it.
>>>>>>>
>>>>>>> It ought to be at least as simple as:
>>>>>>>
>>>>>>> struct Foo(A, B, C){
>>>>>>> A[10] a;
>>>>>>> B b;
>>>>>>> C c;
>>>>>>> void toString(Sink sink){
>>>>>>>    foreach(x; a) sink(x);
>>>>>>>    sink(b);
>>>>>>>    sink(c);
>>>>>>> }
>>>>>>> }
>>>>>>> ... but it's not, you have to create a silly buffer to put all
>>>>>>> your strings in, even if there are 200 million of them and your
>>>>>>> giant string is just going to be written to a file anyway.
>>>>>>>
>>>>>>> I'd like to see version(debug) {} put around Object.toString().
>>>>>>> It's a deathtrap feature that's got no business being used other
>>>>>>> than for debugging.
>>>>>>> </rant>
>>>>>>
>>>>>> First of all, printing stuff "struct.toString()" style is for two things:
>>>>>>
>>>>>>  o  Debugging
>>>>>>  o  Small throwaway code snippets
>>>>>>
>>>>>> The latter mainly being for two purposes:
>>>>>>
>>>>>>  o  Testing quick concepts, trying out library functions, etc.
>>>>>>  o  For the newbie, when he's learning D, but not output formatting.
>>>>>>
>>>>>> No "Real Program" uses this, because there you typically do proper formatting of the output anyway, and almost never print entire structs or objects as such. Instead, rather the information that they represent.
>>>>>
>>>>> How about something like BigInt? Why can't you just print it out?
>>>>
>>>> ?? Why couldn't you?
>>>>
>>>> They're not stored as strings (not Janice's anyway), but I don't understand the question.
>>>
>>> You can write:
>>>
>>> int a, b;
>>> a=10; b=20;
>>> writefln("%d %x", a, b);
>>>
>>> I'd like to be able to write:
>>>
>>> BigInt a, b;
>>> a=10; b=20;
>>> writefln("%d %x", a, b);
>>>
>>> and have it behave exactly the same.
>>   Hmm. My idea was to only have writeln (and not writefln) be
>> automated. (For the small/debugging purpose.)
>>  Andrei seems to be at this, but right now I don't know enough details
>> to say anything. It seems to be an even bigger thing than what I
>> suggested, and knowing he does things in a universal way, one would
>> assume that if a class "wants" to be printed in some way, then maybe
>> there'll be some provisions for it.
>>  But, things like BigInt, that really are classes or structs that have
>> to be printed in a specific way, I have a hard time figuring out how to
>> printe them using write_f_ln.
>>  One way would be to have the format specification (as in "%x") be
>> somehow passed to the toString of the struct/class. Then the class
>> could decide for itself how to be printed in this case.
>>   But even this is stretching it a bit, since some more complicated
>> class/struct might need a more elaborate hint than just one letter. And
>> by that time the whole thing starts to crumble, in usability issues, at
>> least.
>>  One thing we sholuld be wary of is overdesign. BigInt is ok, but by
>> the time we try to include a class called
>> CompleteSimulationOfAnF1RaceCar, we're screwed. :-)  I see no way to
>> incorporate them into writefln or even plain writeln. Or at least, no
>> *use*.
>
> I think it'd be reasonable to limit things to the options available for built-in types. Outside of that, custom formatting functions make a lot of sense. The problem is that toString() _looks_ like it emulates built-in formatting, but it only does '%s'. So it's really beguiling.
>
> BTW, when passing the output to a sink, it should be possible to (say) format your members with '%x' format, but you can't do that by permanently altering sink: it should revert to its previous value once you've sunk your last member. (I think this C++ iostreams got this wrong).

I'm not sure Sink should know anything about formatting. It's up to class designer to decide what custom formatters mean - they may be *very* complex. See my other post about formatting.