我试图通过在我经常使用的语言Clojure中实现算法W来自学Hindley-Milner类型。我遇到let
推断的问题,我不确定我做错了什么,或者我期望的结果是否需要算法以外的东西。< / p>
基本上,使用Haskell表示法,如果我试图推断出它的类型:
\a -> let b = a in b + 1
我明白了:
Num a => t -> a
但我应该得到这个:
Num a => a -> a
同样,我实际上是在Clojure中这样做,但我不相信问题是特定于Clojure的,所以我使用Haskell表示法使其更清晰。当我在Haskell中尝试时,我得到了预期的结果。
无论如何,我可以通过将每个let
转换为函数应用程序来解决该特定问题,例如:
\a -> (\b -> b + 1) a
然后我失去了let
多态性。由于我没有HM的任何先验知识,我的问题是我是否在这里遗漏了一些东西,或者这是否与算法的工作方式有关。
修改
如果有人遇到类似的问题,并想知道我是如何解决的,那么我就是关注Algorith W Step By Step。在第2页的底部,它表示&#34;将 Types 方法扩展到列表有时会很有用。&#34;因为它对我来说并不是强制性的,所以我决定跳过那部分并稍后重新审视。
然后我将ftv
TypeEnv
函数直接翻译成Clojure,如下所示:(ftv (vals env))
。由于我已将ftv
作为cond
表单实施,并且没有seq
的条款,因此它只返回nil
(vals env)
。这当然正是静态类型系统设计要捕获的那种错误!无论如何,我刚刚将ftv
中与env
地图相关的条款重新定义为(reduce set/union #{} (map ftv (vals env)))
并且它有效。
答案 0 :(得分:6)
很难说出错是什么,但我猜你的概括是错误的。
让我们手动输入字词。
__bridge
首先,我们将\a -> let b = a in b + 1
与新的类型变量相关联,例如a
。
然后我们输入a :: t0
。我们也得到b = a
。
但是,这是关键点,我们应该不将b :: t0
的类型概括为b
。这是因为我们只能对环境中没有出现的tyvar进行概括:在这里,我们确实在b :: forall t0. t0
中存在t0
。
如果你对它进行概括,你会得到一种a :: t0
的通用类型。然后b
变为b+1
,整个字词变为b+1 :: forall t0. Num t0 => t0
,因为forall t0 t1. Num t1 => t0 -> t1
和a
的类型不相关(b
,一旦推广,可以被alpha转换为t0
)。