不幸的是,关于这个主题的许多文献都非常密集。我也站在你的立场上。我从编程语言:应用程序和解释中获得了对该主题的第一次介绍
http://www.plai.org/
我将尝试总结抽象的想法,然后是我没有立即发现的细节。首先,类型推断可以被认为是生成然后解决约束。要生成约束,您需要遍历语法树并在每个节点上生成一个或多个约束。例如,如果节点是+
运算符,则操作数和结果都必须是数字。应用函数的节点具有与函数结果相同的类型,依此类推。
对于没有 的语言let
,你可以盲目地通过替换来解决上述约束。例如:
(if (= 1 2)
1
2)
在这里,我们可以说 if 语句的条件必须是布尔值,并且 if 语句的类型与其then
andelse
子句的类型相同。由于我们知道1
并且2
是数字,通过替换,我们知道该if
语句是一个数字。
事情变得令人讨厌的地方,我有一段时间无法理解的是处理 let:
(let ((id (lambda (x) x)))
(id id))
在这里,我们绑定id
了一个函数,该函数返回您传入的任何内容,也称为身份函数。问题是函数参数的类型x
在每次使用时都不同id
。第二个id
是 type 的函数a -> a
,a
可以是任何东西。第一个是类型(a -> a) -> (a -> a)
。这称为 let 多态性。关键是以特定顺序求解约束:首先求解 的定义的约束id
。这将是a -> a
。然后,可以将类型的新的、单独的副本id
替换到每个id
使用位置的约束中,例如a2 -> a2
and a3 -> a3
。
在线资源中不容易解释这一点。他们会提到算法 W 或 M,但不会提到它们在解决约束方面的工作方式,或者为什么它不会对 let 多态性产生误解:这些算法中的每一个都在解决约束时强制执行排序。
我发现这个资源非常有助于将算法 W、M 以及约束生成和求解的一般概念结合在一起。它有点密集,但比许多好:
http://www.cs.uu.nl/research/techreps/repo/CS-2002/2002-031.pdf
那里的许多其他论文也很好:
http://people.cs.uu.nl/bastiaan/papers.html
我希望这有助于澄清一个有点模糊的世界。