在Hindley-Milner型系统中正确形式的letrec?

时间:2015-07-02 07:56:53

标签: functional-programming type-inference lambda-calculus hindley-milner

我无法理解维基百科上给出的HM系统的letrec定义,在这里:https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system#Recursive_definitions

对我来说,规则大致转换为以下算法:

  1. 推断letrec定义部分中所有内容的类型
    1. 将临时类型变量分配给每个已定义的标识符
    2. 以临时类型
    3. 递归处理所有定义
    4. 成对,将结果与原始临时变量统一
  2. 关闭(使用forall)推断类型,将它们添加到基础(上下文)并使用它推断表达式部分的类型
  3. 我遇到这样的程序有问题:

    letrec
     p = (+)     --has type Uint -> Uint -> Uint
     x = (letrec
           test = p 5 5
         in test)
    in x
    

    我正在观察的行为如下:

    • p的定义获取临时类型a
    • x的定义也有一些临时类型,但现在超出了我们的范围
    • x中,test的定义获得临时类型t
    • p从范围获取临时类型a,使用变量的HM规则
    • (f 5)由HM规则处理申请,结果类型为b(以及aUint -> b统一的统一)
    • ((p 5) 5)由相同的规则处理,从而产生更多的统一并输入ca现在结果与Uint -> Uint -> c结合
    • 现在,测试关闭以输入forall c.c
    • in test的变量测试获取具有新变量的类型实例(或forall c.c),与变量的HM规则相符,从而生成test :: d(与{{1}统一(右))
    • 结果test::t实际上有效x(或d,具体取决于统一的情绪)

    问题:t显然应该有x类型,但我认为这两者无法统一生成类型。当Uint的类型被关闭并再次实例化时,我不确定如何克服或连接替换/统一时,会丢失信息。

    知道如何更正算法以正确输入test吗?或者这是HM系统的属性,它根本不会输入这种情况(我怀疑)?

    请注意,标准x::Uint完全可以,但我不想使用let无法处理的递归定义来混淆示例。

    提前致谢

1 个答案:

答案 0 :(得分:0)

回答我自己的问题:

Wiki上的定义是错误的,尽管它至少在某种程度上适用于类型检查。

向HM系统添加递归的最简单和正确的方法是使用fix谓词,定义fix f = f (fix f)和类型forall a. (a->a) -> a。相互递归由双重修复点等处理。

Haskell解决问题的方法(在https://gist.github.com/chrisdone/0075a16b32bfd4f62b7b#binding-groups中描述)(粗略地)为所有函数派生一个不完整的类型,然后再次运行派生以相互检查它们。