April 23, 2009
Georg Wrede wrote:
> Wow!
> 
> What if writeln would automatically call to!string for any object or struct?

That's the plan, I didn't get around to it. I want to do it the right way, i.e. with general streams, not strings.

Andrei
April 23, 2009
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?
Also, I don't know ranges, but your example doesn't seem to make much sense.

> Andrei
April 23, 2009
On Thu, 23 Apr 2009 17:35:59 +0400, Frits van Bommel <fvbommel@remwovexcapss.nl> wrote:

> Denis Koroskin wrote:
>> On Thu, 23 Apr 2009 16:20:03 +0400, Don <nospam@nospam.com> wrote:
>>
>>> 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.
>>>
>>  Absolutely agree, but Sink is not good, either. I have previously
>> suggested the following design (but can't find my post anymore):
>>  A signature of toString() should be as follows:
>>  char[] toString(string format = null, char[] buffer = null);
>>  Bonuses you get from that:
>> You don't have to change your code (aside from toString() returning
>> mutable array now, but it is very easy to fix)
>> It allows you to avoid allocations - just pass a temporary buffer to
>> use!
>
> It'll still allocate if the buffer isn't big enough.
>

Of course, it allows you to avoid allocations, but it doesn't necessarily eliminate them.

> I usually define something like "void streamTo(Sink sink)" in a base
> class if I want non-allocating output. Adding a format string to the
> parameter list should be easy, but I haven't needed it yet.
> I then usually implement toString by passing an appending Sink to that
> method, just so Tango's formatting methods will be able to use it.
>
>
>
> IMHO It'd be pretty nice for the standard formatting systems (both the Tango and Phobos ones) to just call a standard Object method taking (Sink sink, char[] format = null) on objects.
>

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.

That said, I have other use-cases, too, but never once I needed a custom Sink property. Besides, having a Sink object is redundant and makes things more complex in most cases. Compare:

class Number
{
    private int _number;

    // My version
    char[] toString(string format, char[] buffer = null) {
        return std.string.format(buffer, format, _number); // use default formatter, buffer is provided to avoid allocations
    }

    // Your version
    void toString(Sink sink, string format = null) {
        // what should I do here? How do I avoid allocations? I have to duplicate code anyway
        char[16] buffer;
        buffer = std.string.format(buffer, format, _number);
        sink(buffer);
    }
}

How is that better than using external sink? Use composition! toString/streamOut can be implemented on top of my toString:

char[] buffer;
sink(object.toString(format, buffer)); // I believe explicit is better here

Also, this way sink is called directly an may be inlined.

Besides, why is it called toString(), if it doesn't give me an object's string representation???

> Backward compatibility might be tricky though, if you want to support both overriding and calling of toString(). (You can't have the default toString implementation call the format function *and* have the default format function implementation call toString)
>

My version is (almost) backwards compatible.
April 23, 2009
> 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.

Needs simple converter code, that safe you from typing trivial stuff like this:

toString((char[] s) { writeln(s); });

>     void toString(Sink sink, string format = null) {
>         // what should I do here? How do I avoid allocations? I have to duplicate code anyway
>         char[16] buffer;
>         buffer = std.string.format(buffer, format, _number);
>         sink(buffer);

The idea is to add a format() function that takes sink() for output: std.string.format(sink, yourformat, _number);

> Besides, why is it called toString(), if it doesn't give me an object's string representation???


Should be called debug_output() or something similar.
April 23, 2009
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?

BTW to everyone, 'Sink' was not a proposal. I was just saying that almost anything's better than the current toString().
April 23, 2009
Andrei Alexandrescu wrote:
> Georg Wrede wrote:
>> Wow!
>>
>> What if writeln would automatically call to!string for any object or struct?
> 
> That's the plan, I didn't get around to it. I want to do it the right way, i.e. with general streams, not strings.

I'm just impressed.
April 23, 2009
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.

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

s/but/consequently/


Andrei
April 23, 2009

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.

  -- Daniel
April 23, 2009
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.

> BTW to everyone, 'Sink' was not a proposal. I was just saying that almost anything's better than the current toString().
April 23, 2009
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