On Saturday, 25 July 2015 at 08:48:40 UTC, Walter Bright wrote:
On 7/24/2015 11:10 PM, Jonathan M Davis wrote:
So, maybe we should look at something along those lines rather than proliferating the top-level function overloading like we're doing now.

Consider the following pattern, which I see often in Phobos:

    void foo(T)(T t) if (A) { ... }
    void foo(T)(T t) if (!A && B) { ... }

from a documentation (i.e. user) perspective. Now consider:

    void foo(T)(T t) if (A || B)
    {
         static if (A) { ... }
         else static if (B) { ... }
         else static assert(0);
    }

Makes a lot more sense to the user, who just sees one function that needs A or B, and doesn't see the internal logic.

Yeah, though, I believe that Andrei has argued against that every time that someone suggests doing that. IIRC, he wants ddoc to that for you somehow rather than requiring that we write code that way.

And from a test perspective, it's actually a bit ugly to take function overloads and turn them into static ifs, because instead of having separate functions that you can put unittest blocks under, you have to put all of those tests in a single unittest block or put the unittest blocks in a row with comments on them to indicate which static if branch they go with. It also has the problem that the function can get _way_ too long (e.g. putting all of the overloads of find in one function would be a really bad idea).

Alternatively, you could do something like

template foo(T)
    if(A || B)
{
    void foo()(T t)
        if(A)
    {}

    void foo()(T t)
        if(B)
    {}
}

which gives you the simplified template constraint for the documentation, though for better or worse, you'd still get the individual template constraints listed separately for each overload - though given how often each overload needs an explanation, that's not necessarily bad.

And in many cases, what you really have is overlapping constraints rather than truly distinct ones. So, you'd have something like

auto foo(alias pred, R)(R r)
    if(testPred!pred && isInputRange!R && !isForwardRange!R)
{}

auto foo(alias pred, R)(R r)
    if(testPred!pred && isForwardRange!R)
{}

and be turning it into something like

template foo(alias pred)
    if(testPred!pred)
{
    auto foo(R)(R r)
        if(isInputRange!R && !isForwardRange!R)
    {}

    auto foo(R)(R r)
        if(isForwardRange!R)
    {}
}

So, part of the template constraint gets factored out completely. And if you want to factor it out more than that but still don't want to use static if because of how it affects the unit tests, or because you don't want the function to get overly large, then you can just forward it to another function. e.g.

auto foo(alias pred, R)(R r)
    if(testPred!pred && isInputRange!R)
{
    return _foo(pred, r);
}

auto _foo(alias pred, R)(R r)
    if(!isForwardRange!R)
{}

auto _foo(alias pred, R)(R r)
    if(isForwardRange!R)
{}

or go for both the outer template and forwarding, and do

template foo(alias pred)
    if(testPred!pred)
{
    auto foo(R)(R r)
        if(isInputRange!R)
    {
        return _foo(pred, r);
    }

    auto _foo(R)(R r)
        if(!isForwardRange!R)
    {}

    auto _foo(R)(R r)
        if(isForwardRange!R)
    {}
}

We've already created wrapper templates for at least some of the functions in Phobos so that you can partially instantiate them - e.g.

alias myMap = map!(a => a.func());

So, it we're already partially moving stuff up a level in some cases. We just haven't used it as a method to simplify the main template constraint that user sees or to simplify overloads.

I do think that it can make sense to put very similar overloads in a single function with static if branches like you're suggesting, but I do think that it's a bit of a maintenance issue to do it for completely distinct overloads - especially if there are several of them rather than just a couple. But it's still possible to combine their template constraints at a higher level and have overloaded functions rather than simply using static ifs.

- Jonathan M Davis

Reply via email to