在D(LDC2)

时间:2018-04-01 13:52:44

标签: templates d type-inference

我使用Option编写了自定义Algebraic类型。

struct None{};
struct Some(T){
    T t;

    alias t this;

    T get(){
        return t;
    }
};

alias Option(T) = Algebraic!(Some!(T), None);
Option!T some(T)(T t){
    return Option!T(Some!T(t));
}
Option!T none(T)(){
    return Option!T(None());
}

然后我尝试编写一些基本的便利函数:

T getValue(T,uint size)(VariantN!(size, Some!T, None) opt){
    return opt.get!(Some!T).t;
}

bool isDefined(T, uint size)(VariantN!(size, Some!T, None) opt){
    return opt.convertsTo!(Some!T);
}

A match(A,T,uint size)(VariantN!(size, Some!T, None) opt, A delegate(T) some, A delegate() none){
    if(opt.isDefined!(T,size)){
        return some(opt.getValue!(T,size));
    }else{
        return none();
    }
}

当调用match编译器无法推断出模板的正确参数时:

 Option!int test = some(1);
 bool boolReturn = test.match((x) => false, () => true);

有错误:

    Error: template util.match cannot deduce function from argument types !()(VariantN!(4LU, Some!int, None), void, bool function() pure nothrow @nogc @safe), candidates are:
src/util.d(79,3):        util.match(A, T, uint size)(VariantN!(size, Some!T, None) opt, A delegate(T) some, A delegate() none)

错误输出表明match(意为bool delegate(int)(x) => false)的第二个参数推断为void。为什么?

此示例编译(所有相同但x的类型明确给出):

Option!int test = some(1);
bool boolReturn = test.match((int x) => false, () => true);

1 个答案:

答案 0 :(得分:2)

如果在委托中没有给出类型名称,它会将其作为模板(在错误消息中键入为void),在实例化时将具有推断类型....并且在此处它想要被推断为类型T,它本身是基于参数的推断参数。

问题是编译器试图推断出(x)=>模板并同时推断函数调用并且不知道哪一个先做,所以它看起来不够深。如果你在任何一个地方明确提到它,它就会打破这个循环:

//有效  bool boolReturn = test.match!(bool,int)((x)=> false,()=> true);

//有效 test.match((int x)=> false)

但我不确定如何自动完成...我尝试用不同的安排将它们分离,但没有运气......

Phobos通常解决这个问题的方法是将委托放在alias模板参数而不是运行时参数中。将签名更改为:

typeof(none()) match(alias some, alias none, T, uint size)(VariantN!(size, Some!T, None) opt) {

并将通话更改为:

 bool boolReturn = test.match!((x) => false, () => true);

并且编译因为您将推理移动到两个层:首先,编译器接受这些委托,仍然以模板形式作为参数。然后它接受test并找出其类型以推断其他参数。然后它进入正文并实际看到调用some,并在该点内,在体内实例化其参数类型。所以它的工作原理是因为推断类型是在已经从身体外的签名层知道类型T之后完成的。

但是在没有模板功能的情况下这样做....我不知道,我认为解决方案是做两层,所以显式类型在一个或另一个中命名,但我只是没想到那个(然而tbh将会停止尝试,因为我现在还有其他事情要做)。