writeln, UFCS and flip

bearophile bearophileHUGS at lycos.com
Thu Apr 25 08:09:39 PDT 2013


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


More information about the Digitalmars-d-learn mailing list