Haskell 不想输入高级多态性

标签 haskell polymorphism type-inference system-f

我不明白为什么这个程序不能打字:

type Test a = forall z. (a -> z) -> z

cons :: a -> Test a
cons = \a -> \p -> p a

type Identity = forall x. x -> x

t :: Identity
t = \x -> x

c :: Test Identity
c = cons (t :: Identity)

main :: IO ()
main = do{
  print "test"
}

我使用选项 -XRankNTypes与 GHC。

我有以下错误:
Couldn't match type `x0 -> x0' with `forall x. x -> x'
Expected type: Identity
  Actual type: x0 -> x0
In the first argument of `cons', namely `(t :: Identity)'
In the expression: cons (t :: Identity)
In an equation for `c': c = cons (t :: Identity)

有人可以帮助我吗?

最佳答案

RankNTypes 的推理很棘手。尝试注释函数而不是参数。

c :: Test Identity
c = (cons :: Identity -> Test Identity) t

为什么会这样,需要深入研究类型推断算法的复杂性。这是背后的一些直觉。

直观地说,每当一个多态值 x :: forall a. F(a)使用,类型变量 a永远不会自动实例化为多态类型。相反,a被一个“未知”的新变量取代 a0 (范围涵盖单态类型)。然后使用这个变量来生成类型方程,然后使用统一来求解。

例子:
id id :: ??

让我们称这两次出现为 id0id1 .我们得到
id0 id1 :: ?? 
id0 :: forall a. a->a
id1 :: forall a. a->a

实例化新变量
id0 :: a0 -> a0
id1 :: a1 -> a1

统一参数类型:a0 ~ (a1 -> a1) .
id0 :: (a1 -> a1) -> (a1 -> a1)
id1 :: a1 -> a1

申请。
id0 id1 :: a1 -> a1

重新概括。
id0 id1 :: forall a. a->a

请注意,在这种特定情况下,我们可以通过统一 a0 ~ (forall a. a->a) 获得相同的最终结果。相反,并避免生成新的未知 a1 .然而,这不是推理算法中发生的情况。

如果我们求助于手动输入,我们可以实现后一种实例化:
(id :: (forall a. a->a) -> (forall a. a->a)) id

但是,上述讨论也有一些异常(exception)。主要的是 2 级(和 N 级)类型。当 GHC 知道一个函数排名更高时,它的参数会以不同的方式处理:forall - 出现在其类型中的量化变量不会被未知数取代。相反,forall s 被保留,以便它们以后可以与函数期望的类型匹配。

我建议阅读 GHC 用户指南,其中解释了一些正在发生的事情。如果你想要所有血腥的细节,你需要引用描述实际打字规则的论文。 (实际上,在阅读这些之前,我会学习一些更简单系统的背景知识,例如基本的 Hindley-Milner)。

关于Haskell 不想输入高级多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27109280/

相关文章:

haskell - 如何在应用类型默认规则的 ghci 中打印多态函数(或值)的类型?

haskell - 如何链接到本地​​ Haskell 库?

Python 等价于 Haskell 的 [1..](索引列表)

c++ - 使用堆栈分配的对象将异构对象存储在 vector 中

c# - 为什么 C# 构造函数不能推断类型?

list - Haskell 中列表的递归

c++ - rogue like 游戏初始化错误

C++ template covariance polymorphism 收藏

java - 目标类型具有通配符时的泛型方法类型推断

F# 类型推断