我无法理解维基百科上给出的HM系统的letrec定义,在这里:https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system#Recursive_definitions
对我来说,规则大致转换为以下算法:
letrec
定义部分中所有内容的类型
forall
)推断类型,将它们添加到基础(上下文)并使用它推断表达式部分的类型我遇到这样的程序有问题:
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
(以及a
与Uint -> b
统一的统一)((p 5) 5)
由相同的规则处理,从而产生更多的统一并输入c
,a
现在结果与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
无法处理的递归定义来混淆示例。
提前致谢
答案 0 :(得分:0)
回答我自己的问题:
Wiki上的定义是错误的,尽管它至少在某种程度上适用于类型检查。
向HM系统添加递归的最简单和正确的方法是使用fix
谓词,定义fix f = f (fix f)
和类型forall a. (a->a) -> a
。相互递归由双重修复点等处理。
Haskell解决问题的方法(在https://gist.github.com/chrisdone/0075a16b32bfd4f62b7b#binding-groups中描述)(粗略地)为所有函数派生一个不完整的类型,然后再次运行派生以相互检查它们。