Search code examples
templatesddmd

Sending a templated function as an argument to a templated function in D


I'm trying to send D's sort function as a template argument to the pipe function. When I use sort without template arguments it works:

import std.stdio,std.algorithm,std.functional;

void main()
{
    auto arr=pipe!(sort)([1,3,2]);
    writeln(arr);
}

However, when I try to use sort with a template argument:

import std.stdio,std.algorithm,std.functional;

void main()
{
    auto arr=pipe!(sort!"b<a")([1,3,2]);
    writeln(arr);
}

I get an error - main.d(5): Error: template instance sort!("b<a") sort!("b<a") does not match template declaration sort(alias less = "a < b",SwapStrategy ss = SwapStrategy.unstable,Range)

Why does it happen? sort!"b<a" works on it's own, and it has the same arguments and return types as sort, so why does pipe accept sort but not sort!"b<a"? And is there a correct syntax for what I try to do?

UPDATE

OK, I've tried to wrap the sort function. The following code works:

import std.stdio,std.algorithm,std.functional,std.array;

template mysort(string comparer)
{
    auto mysort(T)(T source)
    {
        sort!comparer(source);
        return source;
    }
}

void main()
{
    auto arr=pipe!(mysort!"b<a")([1,3,2]);
    writeln(arr);
}

So why doesn't the original version work? is this because of the extra template parameters sort takes?


Solution

  • Yes it's because of the extra template parameters — specifically the Range parameter. The problem can be reduced to

    size_t sort2(alias f, Range)(Range range)
    {
        return 0;
    }
    alias sort2!"b<a" u;
    

    The instantiation sort!"b<a" will fail because the Range is not determined. The function call sort2!"b<a"([1,2,3]) works because the parameter [1,2,3] can tell the compiler the type Range is int[]. This is known as "implicit function template instantiation (IFTI)". But IFTI only works when it is used as a function. In your use case, sort!"b<a" is instantiated without providing all parameters, thus the error.

    This can be fixed by making the input a function literal, which is just similar to your mysort solution:

     auto arr = pipe!(x => sort!"b<a"(x))([1,3,2]);
    

    Or you could provide all required template parameters. This makes the code very unreadable though.

    auto arr = pipe!(sort!("b<a", SwapStrategy.unstable, int[]))([1,3,2]);