haskell - Haskell 类型推断令人困惑

标签 haskell polymorphism type-inference

我刚刚开始学习 Haskell。由于 Haskell 是静态类型并且具有多态类型推断,因此恒等函数的类型为

id :: a -> a

建议 id 可以采用任何类型作为参数并返回自身。当我尝试时效果很好:

a = (id 1, id True)

我只是假设在编译时,第一个id是Num a::a -> a,第二个id是Bool -> Bool。当我尝试以下代码时,出现错误:

foo f a b = (f a, f b)
result = foo id 1 True

它表明 a 的类型必须与 b 的类型相同,因为它可以很好地工作

result = foo id 1 2

但是 id 参数的类型可以是多态的,因此 a 和 b 可以是不同的类型吗?

最佳答案

好吧,这是 Haskell 类型系统的一个奇怪的角落。这里的问题是有两种方法可以输入推理函数 foo .

-- rank 1
foo :: forall a b. (a -> b) -> a -> a -> (b, b)
foo f a b = (f a, f b)

-- rank 2
foo' :: (forall a. a -> a) -> a -> b -> (a, b)
foo' f a b = (f a, f b)

第二种类型是您想要的,但第一种类型是您得到的。正如 amalloy 所指出的,第二种类型是 2 级类型(我们将忽略这两者的含义,但如果您想要对等级有一个很好的解释,请阅读 "Practical type inference for arbitrary-rank types" 中的介绍 – 不要被PDF 文件的学术性质作为开头,写得易于理解且清晰)。

我们暂时推迟更高等级类型的定义,只是说问题是 GHC 无法推断等级 2 类型。引用论文:

Complete type inference is known to be undecidable for higher-rank (impredicative) type systems, but in practice programmers are more than willing to add type annotations to guide the type inference engine, and to document their code....

Kfoury and Wells show that typeability is decidable for rank ≤ 2, and undecidable for all ranks ≥ 3 (Kfoury & Wells, 1994). For the rank-2 fragment, the same paper gives a type inference algorithm. This inference algorithm is somewhat subtle, does not interact well with user-supplied type annotations, and has not, to our knowledge, been implemented in a production compiler.

不可判定意味着不存在总是能得出正确的是或否决策的算法。所以你就知道了:不可能推断出 3 级或更高的类型,而且推断出 2 级类型也太难了。

现在,回到排名 2。(forall a. a -> a)这就是它排名第二的原因。 There's already an excellent Stack Overflow question about what the forall keyword means所以我会向您推荐这一点,但这基本上意味着您可以调用 f af b在表达式 (f a, f b) 中同时ab是不同的类型,这就是你在所有这些困惑之前首先想要的。

最后一件事:您通常看不到 forall 的原因GHCi 中的 s 是任何 forall最外层范围上的 s 被忽略。所以forall a b. (a -> b) -> a -> a -> (b, b)相当于 (a -> b) -> a -> a -> (b, b) .

总的来说,这是语言的一个痛点,而且解释得不好。

(在评论中向@amalloy 致敬。)

关于haskell - Haskell 类型推断令人困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36587571/

相关文章:

java - 类型推断是否适用于构建器?

rust - 无法创建 hyper::Client,因为编译器无法推断出足够的类型信息

java - 为什么 Stream#reduce 不隐式接受处理父类(super class)型元素的累积函数?

haskell - 使用特定顺序将映射序列化为 YAML

Java:应用于 Map 泛型类型的多态性

c++ - 使用具有指向派生类对象的指针的基类指针数组调用派生类方法

java - Protostuff 运行时模式和多态性问题

haskell - 运行 Literate Haskell 代码时没有 (GHC.Base.Alternative Parser) 错误实例

haskell - 将表示二进制数的字符串转换为以 10 为基数的字符串 haskell

haskell - 使用反射和 DataKinds 进行类型推断