exa*_*exa 11 functional-programming type-inference lambda-calculus hindley-milner
我无法理解维基百科上给出的HM系统的letrec定义,这里:https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system#Recursive_definitions
对我来说,规则大致转换为以下算法:
letrec
定义部分中
所有内容的类型forall
)推断类型,将它们添加到基础(context)并用它推断表达式部分的类型我遇到这样的程序有问题:
letrec
p = (+) --has type Uint -> Uint -> Uint
x = (letrec
test = p 5 5
in test)
in x
Run Code Online (Sandbox Code Playgroud)
我观察的行为如下:
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
(与test::t
右侧统一)x
有效类型d
(或者t
,取决于统一的情绪)问题:x
显然应该有类型Uint
,但我认为这两者无法统一生成类型.当test
关闭的类型和再次实例化时,我不确定如何克服或连接替换/统一时丢失信息.
知道如何纠正算法以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中描述)是(粗略地)为所有函数派生一个不完整的类型,然后再次运行派生以相互检查它们。