Thread overview
Output range and writeln style functions
Jan 22, 2017
Jon Degenhardt
Jan 23, 2017
Ali Çehreli
Jan 23, 2017
Jon Degenhardt
Jan 23, 2017
Ali Çehreli
Jan 23, 2017
Jon Degenhardt
January 22, 2017
I've been increasingly using output ranges in my code (the "component programming" model described in several articles on the D site). It works very well, except that it would often be more convenient to use writeln style functions rather than 'put'. Especially when you start by drafting a sketch of code using writeln functions, then convert it an output range.

Seems an obvious thing, I'm wondering if I missed something. Are there ways to use writeln style functions with output ranges?

--Jon
January 23, 2017
On 01/22/2017 01:54 PM, Jon Degenhardt wrote:
> I've been increasingly using output ranges in my code (the "component
> programming" model described in several articles on the D site). It
> works very well, except that it would often be more convenient to use
> writeln style functions rather than 'put'. Especially when you start by
> drafting a sketch of code using writeln functions, then convert it an
> output range.
>
> Seems an obvious thing, I'm wondering if I missed something. Are there
> ways to use writeln style functions with output ranges?
>
> --Jon

I don't think I understand the question. :)

If you need a variadic put(), then I've come up with the following mildly tested AllAppender. Just as a reminder, I've also used std.range.tee that allows tapping into the stream to see what's flying through:

import std.array : Appender, appender;
import std.stdio : writeln;

struct AllAppender(T) {
    Appender!T app;
    alias app this;

    void put(Args...)(Args args) {
        foreach (arg; args) {
            static if (__traits(compiles, app.put(arg))) {
                app.put(arg);
            }
            else {
                import std.conv : to;
                app.put(arg.to!T);
            }
        }
    }
}

import std.range : isOutputRange;
static assert(isOutputRange!(AllAppender!string, int));
static assert(isOutputRange!(AllAppender!string, double));

auto allAppender(T)() {
    return AllAppender!T();
}

void main() {
    auto a = allAppender!string();
    a.put(1, "hello");

    import std.range : tee;
    import std.algorithm : copy;
    [ 10, 20, 30 ]
        .tee!(e => writeln("appending ", e))
        .copy(a);

    writeln(a.data);
}

Ali

January 23, 2017
On Monday, 23 January 2017 at 08:03:14 UTC, Ali Çehreli wrote:
> On 01/22/2017 01:54 PM, Jon Degenhardt wrote:
>> I've been increasingly using output ranges in my code (the "component
>> programming" model described in several articles on the D site). It
>> works very well, except that it would often be more convenient to use
>> writeln style functions rather than 'put'. Especially when you start by
>> drafting a sketch of code using writeln functions, then convert it an
>> output range.
>>
>> Seems an obvious thing, I'm wondering if I missed something. Are there
>> ways to use writeln style functions with output ranges?
>>
>> --Jon
>
> I don't think I understand the question. :)
>
> If you need a variadic put(), then I've come up with the following mildly tested AllAppender. Just as a reminder, I've also used std.range.tee that allows tapping into the stream to see what's flying through:
> 
> [snip]
>
> Ali

So I guess the is answer is "no" :)

It's mainly about consistency of the output primitives. Includes variadic args, formatting, and names of the primitives. I keep finding myself starting with something like:

    void writeLuckyNumber(string name, int luckyNumber)
   {
       writefln("Hello %s, your lucky number is %d", name, luckyNumber);
   }

and then re-factoring it as:

   void writeLuckyNumber(OutputRange)
       (OutputRange outputStream, string name, int luckyNumber)
       if (isOutputRange!(OutputRange, char))
   {
       import std.format;
       outputStream.put(
           format("Hello %s, your lucky number is %d\n", name, luckyNumber));
   }

Not bad, but the actual output statements are a bit harder to read, especially if people reading your code are not familiar with output ranges. So, what I'm really wondering is if there is built-in way to get closer to:

      outputStream.writefln(...);

 that I've overlooked.


--Jon
January 23, 2017
On 01/23/2017 12:48 PM, Jon Degenhardt wrote:
> On Monday, 23 January 2017 at 08:03:14 UTC, Ali Çehreli wrote:
>> On 01/22/2017 01:54 PM, Jon Degenhardt wrote:
>>> I've been increasingly using output ranges in my code (the "component
>>> programming" model described in several articles on the D site). It
>>> works very well, except that it would often be more convenient to use
>>> writeln style functions rather than 'put'. Especially when you start by
>>> drafting a sketch of code using writeln functions, then convert it an
>>> output range.
>>>
>>> Seems an obvious thing, I'm wondering if I missed something. Are there
>>> ways to use writeln style functions with output ranges?
>>>
>>> --Jon
>>
>> I don't think I understand the question. :)
>>
>> If you need a variadic put(), then I've come up with the following
>> mildly tested AllAppender. Just as a reminder, I've also used
>> std.range.tee that allows tapping into the stream to see what's flying
>> through:
>>
>> [snip]
>>
>> Ali
>
> So I guess the is answer is "no" :)
>
> It's mainly about consistency of the output primitives. Includes
> variadic args, formatting, and names of the primitives. I keep finding
> myself starting with something like:
>
>     void writeLuckyNumber(string name, int luckyNumber)
>    {
>        writefln("Hello %s, your lucky number is %d", name, luckyNumber);
>    }
>
> and then re-factoring it as:
>
>    void writeLuckyNumber(OutputRange)
>        (OutputRange outputStream, string name, int luckyNumber)
>        if (isOutputRange!(OutputRange, char))
>    {
>        import std.format;
>        outputStream.put(
>            format("Hello %s, your lucky number is %d\n", name,
> luckyNumber));
>    }
>
> Not bad, but the actual output statements are a bit harder to read,
> especially if people reading your code are not familiar with output
> ranges. So, what I'm really wondering is if there is built-in way to get
> closer to:
>
>       outputStream.writefln(...);
>
>  that I've overlooked.
>
>
> --Jon

If it's about formatted output then perhaps formattedWrite?

  https://dlang.org/phobos/std_format.html#.formattedWrite

The same function is used with stdout and an Appender:

import std.stdio;
import std.range;

void writeLuckyNumber(OutputRange)
(OutputRange outputStream, string name, int luckyNumber)
if (isOutputRange!(OutputRange, char))
{
    import std.format : formattedWrite;
    formattedWrite(outputStream, "Hello %s, your lucky number is %d\n", name, luckyNumber);
}

void main() {
    writeLuckyNumber(stdout.lockingTextWriter, "Jon", 42);

    auto app = appender!string();
    writeLuckyNumber(app, "Jon", 42);

    writeln(app.data);
}

Ali

January 23, 2017
On Monday, 23 January 2017 at 22:20:59 UTC, Ali Çehreli wrote:
> On 01/23/2017 12:48 PM, Jon Degenhardt wrote:
> [snip]
> > So, what I'm really wondering is if there is built-in  way
>>  to get closer to:
>>
>>       outputStream.writefln(...);
>>
>
> If it's about formatted output then perhaps formattedWrite?
>
>   https://dlang.org/phobos/std_format.html#.formattedWrite
>
> The same function is used with stdout and an Appender:
>
> [snip]
>
> Ali

Oh, that is better, thanks!

--Jon