www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - writeln, UFCS and flip

reply "bearophile" <bearophileHUGS lycos.com> writes:
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
Apr 25 2013
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
This is not a real use case because the reduce!() of Phobos will 
be fixed for UCFS, but sometimes you can't swap the arguments of 
a function:


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() {
     reduce!q{a * b}(1, iota(1, 10))
     .writeln;

     iota(1, 10)
     .flip2!(reduce!q{a * b})(1)
     .writeln;
}


Bye,
bearophile
Apr 25 2013
parent reply "ixid" <nuaccount gmail.com> writes:
Doesn't binaryReverseArgs in std.functional already do this? Flip 
is a much, much better name though.
Apr 25 2013
next sibling parent "ixid" <nuaccount gmail.com> writes:
On Thursday, 25 April 2013 at 16:20:39 UTC, ixid wrote:
 Doesn't binaryReverseArgs in std.functional already do this? 
 Flip is a much, much better name though.
Though I still think write functions passing on the data would be useful, letting you avoid needless temporaries just so you can write the output of a function. It's useful for normal code as well as UFCS. string temp = myFun(); temp.writeln; result ~= temp ~ "\n";
Apr 25 2013
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
On Thursday, 25 April 2013 at 16:20:39 UTC, ixid wrote:
 Doesn't binaryReverseArgs in std.functional already do this?
Right, it's the flip I have suggested here. My flip2 is a little different. I will think about this.
 Though I still think write functions passing on the data would 
 be useful, letting you avoid needless temporaries just so you 
 can write the output of a function.
OK. Bye, bearophile
Apr 25 2013
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 04/25/2013 05:09 PM, bearophile wrote:
 ...

 What do you think?

 ...
I think what you call flip2 should be called flip.
Apr 25 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Timon Gehr:

 I think what you call flip2 should be called flip.
Why? Bye, bearophile
Apr 26 2013
parent reply "ixid" <nuaccount gmail.com> writes:
On Friday, 26 April 2013 at 10:52:09 UTC, bearophile wrote:
 Timon Gehr:

 I think what you call flip2 should be called flip.
Why? Bye, bearophile
flip2 is the more general and useful function with new functionality so there's no need for what was flip and we should call the new function 'flip'.
Apr 26 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
ixid:

 flip2 is the more general and useful function with new 
 functionality so there's no need for what was flip and we 
 should call the new function 'flip'.
I don't agree. Maybe you are missing something. I expect a function named "flip" to do: flip!foo(a, b, c) === foo(c, b, a) flip!foo(a, b, c, d) === foo(d, c, b, a) While flip2 does: flip2!foo(a, b, c) === foo(b, a, c) flip2!foo(a, b, c, d) === foo(b, a, c, d) Bye, bearophile
Apr 26 2013
parent reply "Tyro[17]" <nospam home.com> writes:
On 4/26/13 8:59 AM, bearophile wrote:
 ixid:

 flip2 is the more general and useful function with new functionality
 so there's no need for what was flip and we should call the new
 function 'flip'.
I don't agree. Maybe you are missing something. I expect a function named "flip" to do: flip!foo(a, b, c) === foo(c, b, a) flip!foo(a, b, c, d) === foo(d, c, b, a)
I would expect this to be called reverse
 While flip2 does:

 flip2!foo(a, b, c) === foo(b, a, c)
 flip2!foo(a, b, c, d) === foo(b, a, c, d)
and this rotate.... Why flip in the first place?
 Bye,
 bearophile
Apr 26 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Tyro[17]:

 While flip2 does:

 flip2!foo(a, b, c) === foo(b, a, c)
 flip2!foo(a, b, c, d) === foo(b, a, c, d)
and this rotate....
Really? Just swapping the first two arguments and leaving the others at their place is for a "rotate"?
 Why flip in the first place?
I don't know, it's the name used in the Haskell Prelude. Bye, bearophile
Apr 26 2013
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 04/26/2013 10:15 PM, bearophile wrote:
 Tyro[17]:

 While flip2 does:

 flip2!foo(a, b, c) === foo(b, a, c)
 flip2!foo(a, b, c, d) === foo(b, a, c, d)
and this rotate....
Really? Just swapping the first two arguments and leaving the others at their place is for a "rotate"?
 Why flip in the first place?
I don't know, it's the name used in the Haskell Prelude.
And there it flips the first two arguments (of a curried function, since this is the standard for Haskell).
Apr 26 2013
prev sibling parent reply "Tyro[17]" <nospam home.com> writes:
On 4/26/13 4:15 PM, bearophile wrote:
 Tyro[17]:

 While flip2 does:

 flip2!foo(a, b, c) === foo(b, a, c)
 flip2!foo(a, b, c, d) === foo(b, a, c, d)
and this rotate....
Really? Just swapping the first two arguments and leaving the others at their place is for a "rotate"?
Actually, by brain was thinking it but my eyes saw something completely different. You are correct, this what I would expect flip to do. Thanks.
 Why flip in the first place?
I don't know, it's the name used in the Haskell Prelude. Bye, bearophile
Apr 26 2013
parent reply "Tyro[17]" <nospam home.com> writes:
On 4/26/13 5:09 PM, Tyro[17] wrote:
 On 4/26/13 4:15 PM, bearophile wrote:
 Tyro[17]:

 While flip2 does:

 flip2!foo(a, b, c) === foo(b, a, c)
 flip2!foo(a, b, c, d) === foo(b, a, c, d)
and this rotate....
Really? Just swapping the first two arguments and leaving the others at their place is for a "rotate"?
Actually, by brain was thinking it but my eyes saw something completely different. You are correct, this what I would expect flip to do. Thanks.
 Why flip in the first place?
I don't know, it's the name used in the Haskell Prelude. Bye, bearophile
With that clarification, I just have one more question, since flip does what is intuitively implied by a reverse function and flip2 does what is intuitively implied by a flip function, why not name them as such? Understand the Haskell influence and all but, influence notwithstanding, this is D so why not designate functions in a manner that intuitively imply functionality? I would expect a flip and flip2 if both accomplish the exact same thing but both outperforms the other in certain scenarios thus warranting both to be present. Andrew
Apr 26 2013
parent "bearophile" <bearophileHUGS lycos.com> writes:
Tyro[17]:

 With that clarification, I just have one more question, since 
 flip does what is intuitively implied by a reverse function
OK, better to name it reversedArgs :-)
 and flip2 does what is intuitively implied by a flip function,
flip2 swaps just the fist two arguments and leaves the others at their place.
 I would expect a flip and flip2 if both accomplish the exact 
 same thing but both outperforms the other in certain scenarios 
 thus warranting both to be present.
I think that the caos of this thread shows that keeping both will cause confusion :-(
 why not name them as such? Understand the Haskell influence and 
 all but, influence notwithstanding, this is D so why not 
 designate functions in a manner that intuitively imply 
 functionality?
I am starting to agree with Andrei. Short function names are handy, but in many situations the semantic confusion they can bring is not worth it. More food for thought for me. Bye, bearophile
Apr 26 2013