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