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

Correct form of letrec in Hindley-Milner type system?

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

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

  1. 推断 letrec 定义部分中所有内容的类型
    1. 为每个定义的标识符分配临时类型变量
    2. 递归处理所有具有临时类型的定义
    3. 成对,将结果与原来的临时变量统一
  2. 关闭(使用forall)推断类型,将它们添加到基础(上下文)并用它推断表达式部分的类型

我在使用这样的程序时遇到了问题:

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

我观察到的行为如下:

问题:x 显然应该有类型 Uint,但我看不出这两个可以统一产生类型。当 test 的类型关闭并再次实例化时会丢失信息,我不确定如何克服或连接 substitutions/unifications。

知道应该如何更正算法以正确生成 x::Uint 打字吗?或者这是一个 属性 的 HM 系统,它根本不会输入这种情况(我怀疑)?

请注意,这对于标准 let 来说完全没问题,但我不想用 let 无法处理的递归定义来混淆示例。

提前致谢

回答我自己的问题:

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

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

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