在类型推断中需要“统一”的最简单的例子

时间:2009-07-15 19:01:30

标签: c# scheme type-inference unification

我正试图了解如何实现类型推断。 特别是,我不太清楚“统一”的繁重发挥在何处/为何发挥作用。

我将举一个“伪C#”的例子来帮助澄清:

这种天真的方式是这样的:

假设您将程序“解析”到表达式树中,以便可以使用以下命令执行:

interface IEnvironment
{
    object lookup(string name);
}

interface IExpression
{
    // Evaluate this program in this environment
    object Evaluate(IEnvironment e);
}

因此可以通过以下方式实现“乘法”之类的内容:

class Multiply : IExpression
{
    IExpression lhs;
    IExpression rhs;
    // etc.
    public object Evaluate(IEnvironment e)
    {
        // assume for the moment C# has polymorphic multiplication
        return lhs.Evaluate(e) * rhs.Evaluate(e);
    }
}

然后要“实现”类型推断,您可以执行以下操作:

interface ITypeEnvironment
{
    Type getType(string name);
}

interface IExpression
{
    //as before
    object Evaluate(IEnvironment e);
    // infer type
    Type inferType(ITypeEnvironment typeEnvironment);
}

然后“乘法”的类型推断可能就像:

class Multiply : IExpression
{
    IExpression lhs;
    IExpression rhs;

    // ... 
    public Type inferType(ITypeEnvironment typeEnvironment)
    {
        Type lhsType = lhs.inferType(typeEnvironment);
        Type rhsType = rhs.inferType(typeEnvironment);
        if(lhsType != rhsType)
             throw new Exception("lhs and rhs types do not match");

        // you could also check here that lhs/rhs are one of double/int/float etc.
        return lhsType;
    }
}

lhs和rhs可能是简单的常量,或者是在环境中查找的“变量”:

class Constant : IExpression
{
    object value;
    public Type inferType(ITypeEnvironment typeEnvironment)
    {
        return value.GetType(); // The type of the value;
    }
    public object Evaluate(IEnvironment environment)
    {
        return value;
    }
}

class Variable : IExpression
{
    string name;
    public Type inferType(ITypeEnvironment typeEnvironment)
    {
        return typeEnvironment.getType(name);
    }
    public object Evaluate(IEnvironment environment)
    {
        return environment.lookup(name);
    }
}

但在这方面我们最终无需“统一”算法。

所以,显然,我的例子不够复杂。它需要更高阶的功能吗?我们需要“参数多态”吗?

实际需要“统一”来正确推断表达式类型的最简单的例子是什么。

Scheme中的一个例子是理想的(即一个非常小的Scheme程序的例子,你需要统一来正确地推断出s表达式的类型)。

3 个答案:

答案 0 :(得分:5)

让我完全忽略您的示例,并举例说明我们在C#中进行方法类型推断的位置。 (如果您对此主题感兴趣,那么我建议您阅读我博客的“type inference”存档。)

考虑:

void M<T>(IDictionary<string, List<T>> x) {}

这里我们有一个通用方法M,它采用一个将字符串映射到T列表的字典。假设你有一个变量:

var d = new Dictionary<string, List<int>>() { ...initializers here... };
M(d);

我们在没有提供类型参数的情况下调用M<T>,因此编译器必须推断它。

编译器是通过Dictionary<string, List<int>>“统一”IDictionary<string, List<T>>来实现的。

首先,它确定Dictionary<K, V>实现IDictionary<K, V>

由此我们推断出Dictionary<string, List<int>>实现了IDictionary<string, List<int>>

现在我们在IDictionary部分进行了匹配。我们用字符串统一字符串,这显然都很好但我们从中学不到任何东西。

然后我们将List与List统一起来,并意识到我们必须再次递归。

然后我们用T统一int,并意识到int是T的绑定。

类型推断算法逐渐消失,直到它不再有进展,然后它开始从推论中进一步推断。 T上唯一的绑定是int,所以我们推断出调用者必须要T为int。所以我们打电话给M<int>

这是清楚的吗?

答案 1 :(得分:2)

假设我们有一个功能

f(x,y)

其中x可能是例如FunctionOfTwoFunctionsOfInteger 或者它可能是FunctionOfInteger。

假设我们传入

f(g(u,2),g(1,z))

现在统一,u绑定为1,z绑定为2,因此第一个参数是FunctionOfTwoFunctionsOfInteger。

所以,我们必须推断出x和y的类型都是FunctionOfTwoFunctionsOfInteger

我对C#不太熟悉,但是对于lambda表达式(或等效的委托或其他),这应该是可能的。

有关类型推理在提高定理证明速度方面非常有用的示例,请查看“Schubert的Steamroller”

http://www.rpi.edu/~brings/PAI/schub.steam/node1.html

“自动推理期刊”有一个问题,专门讨论这个问题的解决方案和公式,其中大部分涉及定理证明系统中的类型推理:

http://www.springerlink.com/content/k1425x112w6671g0/

答案 2 :(得分:2)

public Type inferType(ITypeEnvironment typeEnvironment)
{
    return typeEnvironment.getType(name);
}

如果您不知道变量的类型怎么办?这就是类型推断的重点,对吗?像这样非常简单(用某种伪代码语言):

function foo(x) { return x + 5; }

你不知道x的类型,直到你推断添加,并意识到它必须是一个整数,因为它被添加到一个整数,或类似的东西。

如果你有另外一个这样的功能怎么办:

function bar(x) { return foo(x); }

在找出x的类型之前,您无法确定foo的类型,等等。

因此,当您第一次看到变量时,您必须为变量指定一些占位符类型,然后,当该变量传递给某种函数或某物时,您必须使用参数“统一”它功能的类型。