writeln is able to write a whole range:

import std.stdio, std.algorithm, std.range;

void main() {
    10.iota.map!(x => x ^^ 2).writeln;
}


Output:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

- - - - - - - - - - - - - - - -

But if you want to format the range in some way you have to change the order of things, this is not nice:


import std.stdio, std.algorithm, std.range;

void main() {
    writefln("%(%d %)", 10
                        .iota
                        .map!(x => x ^^ 2));
}


Output:
0 1 4 9 16 25 36 49 64 81

- - - - - - - - - - - - - - - -

To solve that little problem I suggested to add some new printing functions to Phobos:

http://d.puremagic.com/issues/show_bug.cgi?id=9882


auto uWrite(T)(T data) {
    write(data);
    return data;
}

auto uWriteln(T)(T data) {
    writeln(data);
    return data;
}

auto uWritef(T, TS)(T data, TS format) if (isSomeString!TS) {
    writef(format, data);
    return data;
}

auto uWritefln(T, TS)(T data, TS format) if (isSomeString!TS) {
    writefln(format, data);
    return data;
}

- - - - - - - - - - - - - - - -

Now I have invented a different solution (similar to the "flip" function of Haskell):


import std.stdio, std.algorithm, std.range;

/// flip!foo(a, b) === foo(b, a)
template flip(alias callable) {
    auto flip(T2, T1)(T2 y, T1 x) {
        return callable(x, y);
    }
}

void main() {
    10.iota.map!(x => x ^^ 2).flip!writefln("%(%d %)");
}

You can also write that main() like this:

void main() {
    10
    .iota
    .map!(x => x ^^ 2)
    .flip!writefln("%(%d %)");
}


(I have used a template+function so flip!foo is usable as argument for some algorithms.)

flip is a much more general solution, and it's useful in other situations.

In theory "callable" should have a template constraint like "isCallable!callable", but in practice I think you can't use it for writeln and other templated functions.

- - - - - - - - - - - - - - - -

An alternative design flips the first two arguments of two or more arguments:



import std.stdio, std.algorithm, std.range;

/// flip2!foo(a, b, c...) === foo(b, a, c...)
template flip2(alias callable) {
    auto flip2(T2, T1n...)(T2 y, T1n others)
    if (T1n.length > 0) {
        return callable(others[0], y, others[1 .. $]);
    }
}

void main() {
10.iota.map!(x => x ^^ 2).flip2!writefln("%(%d %) %s", "good");
}


I have named it flip2 because I expect this to be true:

flip!foo(a, b, c) === foo(c, b, a)

so the name "foo2" reminds me that it flips only the first two arguments.

What do you think?

Bye,
bearophile

Reply via email to