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.

Reply via email to