A Small Contribution to Phobos

Brad Anderson eco at gnuk.net
Sat Jun 1 22:23:10 PDT 2013


On Sunday, 2 June 2013 at 02:57:56 UTC, Meta wrote:
> I saw a thread a few days ago about somebody wanting a few 
> UFCS-based convenience functions, so I thought that I'd take 
> the opportunity to make a small contribution to Phobos. 
> Currently I have four small functions: each, exhaust, perform, 
> and tap, and would like some feedback.
>
> each is designed to perform operations with side-effects on 
> each range element. To actually change the elements of the 
> range, each element must be accepted by reference.
>
> Range each(alias fun, Range)(Range r)
> if (isInputRange!(Unqual!Range))
> {
>     alias unaryFun!fun _fun;
>     foreach (ref e; r)
>     {
>         fun(e);
>     }
>
>     return r;
> }
>
> //Prints [-1, 0, 1]
> [1, 2, 3].each!((ref i) => i -= 2).writeln;
>
>
> exhaust iterates a range until it is exhausted. It also has the 
> nice feature that if range.front is callable, exhaust will call 
> it upon each iteration.
>
> Range exhaust(Range)(Range r)
> if (isInputRange!(Unqual!Range))
> {
>
>     while (!r.empty)
>     {
>         r.front();
>         r.popFront();
>     }
>
>     return r;
> }
>
> //Writes "www.dlang.org". x is an empty MapResult range.
> auto x = "www.dlang.org"
>          .map!((c) { c.write; return false; })
>          .exhaust;
>
> //Prints []
> [1, 2, 3].exhaust.writeln;
>
>
> perform is pretty badly named, but I couldn't come up with a 
> better one. It can be inserted in a UFCS chain and perform some 
> operation with side-effects. It doesn't alter its argument, 
> just returns it for the next function in the chain.
>
> T perform(alias dg, T)(ref T val)
> {
>     dg();
>
>     return val;
> }
>
> //Prints "Mapped: 2 4"
> [1, 2, 3, 4, 5]
> .filter!(n => n < 3)
> .map!(n => n * n)
> .perform!({write("Mapped: ");})
> .each!(n => write(n, " "));
>
>
> Lastly is tap, which takes a value and performs some mutating 
> operation on it. It then returns the value.
>
> T tap(alias dg, T)(auto ref T val)
> {
>     dg(val);
>
>     return val;
> }
>
> class Foo
> {
>     int x;
>     int y;
> }
>
> auto f = (new Foo).tap!((f)
> {
>     f.x = 2;
>     f.y = 3;
> });
>
> //Prints 2 3
> writeln(f.x, " ", f.y);
>
> struct Foo2
> {
>     int x;
>     int y;
> }
>
> //Need to use ref for value types
> auto f2 = Foo2().tap!((ref f)
> {
>     f.x = 3;
>     f.y = 2;
> });
>
> //Prints 3 2
> writeln(f2.x, " ", f2.y);
>
>
> Do you think these small functions have a place in Phobos? I 
> think each and exhaust would be best put into std.range, but 
> I'm not quite sure where perform and tap should go. Also, 
> there's that horrible name for perform, for which I would like 
> to come up with a better name.

You may find this forum discussion from several months ago 
interesting.

http://forum.dlang.org/post/kglo9d$rjf$1@digitalmars.com

Confusingly, your each() seems to be fairly similar to what 
Andrei wanted tap() used for.  Andrei didn't care for the tap() 
you propose but loved the idea of a tap() function that works 
like unix tee.

I like exhaust() as I just had to write something similar.  I 
like perform() just because I love UFCS range chains and anything 
to avoid those extra statements is alright in my book.  This is 
probably not a majority opinion though.  I can't think of a 
better name either though.


More information about the Digitalmars-d mailing list