September 28

It’s still work-in-progress, but this is the current state: An elaborate lambda (one starting with function or delegate) can optionally carry a template parameter list and in that case, optionally a constraint. How embarrassing is it that C++ has explicit template parameters for lambdas for 4 years now and D doesn’t have them?

The syntax:

function template(TemplateParameters) ReturnType? Parameters? Attributes? Constraint? Contracts? Body

When you use template, the parameter list is a regular function parameter list, i.e. there is no (x) with automatic type. This is because it can’t be done in general. But what it gives you is variadic lambdas!

template tt(alias f)
{
    enum tt = f(1, "abc", true, 3.14);
}

enum x = tt!(function template(Ts...)(Ts args) {
    import std.conv : to;
    string result;
    static foreach (alias arg; args)
    {
        result ~= arg.to!string;
    }
    return result;
});
pragma(msg, x); // 1abctrue3.14

For example, this is what I want to be able to do:

import std.traits : isIntegral;
static assert(allSatisfy!(
    function template(T) => isIntegral!T && T.max > 20_000,
    short,
    int
));
static assert(!allSatisfy!(
    function template(T) => isIntegral!T && T.max > 20_000,
    byte,
    short,
    int
));

Unfortunately, this doesn’t work. T is not in scope in the body. For whatever reason.

However, it is in scope for the parameter list. So I can do:

import std.traits : isIntegral;
static assert(allSatisfy!(
    function template(X)(bool result = isIntegral!X && X.max > 20_000) => result,
    short,
    int
));
static assert(!allSatisfy!(
    function template(X)(bool result = isIntegral!X && X.max > 20_000) => result,
    byte,
    short,
    int
));

Now, I have to admit, allSatisfy isn’t std.meta.allSatisfy because that one works with enum bool templates only, not with functions that require being called.

But the above is not wishful thinking. I implemented my own allSatisfy that should work with enum bool templates, but also function pointers and delegates that can be called with no arguments and return bool.

template allSatisfy(alias cond, Ts...)
{
    static foreach (T; Ts)
    {
        static if (!is(typeof(allSatisfy) == bool)) // not yet defined
        {
            static if (cast(bool)cond!T) // true when `cond` is a FP or delegate …
            {
                // … and if it is …
                static if (is(typeof( cond!T) == delegate)
                    ||     is(typeof(*cond!T) == function))
                {
                    // … and the call returns `false` …
                    static if (!cond!T())
                    {
                        // … `T` failed.
                        enum bool allSatisfy = false;
                    }
                }
            }
            else
            {
                 enum bool allSatisfy = false;
            }
        }
    }
    static if (!is(typeof(allSatisfy) == bool)) // if not yet defined
    {
        enum allSatisfy = true;
    }
}

For reasons beyond my understanding, the cast(bool) is needed before const!T.