为什么Haskell中`f x = x x`与`g x = x x x x x`的类型完全相同?
现象背后的核心原因
这个现象本质是由你给参数x声明的强多态类型决定的:
- 你给
x指定的类型是forall a b. a -> b,这是一个全称量化的Rank-2类型,含义是:x可以接收任意类型的输入,同时返回任意你指定类型的输出。 - 因为输出类型
b没有任何约束,它既可以是普通的基础类型(比如Int、String),也可以是任意复杂度的函数类型,这是所有调用形式都能通过类型检查的核心前提。
不同调用形式的类型推导过程
我们可以分别对应你举的两个例子拆解推导逻辑:
1. 调用形式x x的推导
你需要最终返回任意类型c,只需要做一次类型实例化:
把x的输入类型a实例化为(forall a0 b0. a0 -> b0)(也就是参数x自身的类型),输出类型b实例化为你需要的返回值类型c。此时x接收另一个x作为输入,刚好返回c,完全符合类型要求。
2. 调用形式x x x x x的推导
不管串多少个x,你只需要按需实例化前面x的输出类型为匹配后续参数的函数类型即可:
- 第一个
x的输出类型实例化为:(forall a0 b0. a0 -> b0) -> (forall a1 b1. a1 -> b1) -> (forall a2 b2. a2 -> b2) -> (forall a3 b3. a3 -> b3) -> c,也就是一个接收4个x类型参数、最终返回c的函数 - 第一个
x接收第二个x作为输入后,返回上面的多参数函数 - 后续的3个
x依次作为参数喂给这个返回的函数,最终就得到了c,完全符合类型要求。
额外补充
实际上forall a b. a -> b这个类型在Haskell中没有合法的非底层居民,只有undefined、error这类永不返回的底层值才能匹配这个类型。不管你写多少个x的调用,这个函数本质上都不可能正常返回,类型系统只需要校验调用的类型合法性,不需要关心参数是否有实际的非底层实现,所以不管调用串里有多少个x,最终的函数类型永远都是(forall a b. a -> b) -> c。
内容的提问来源于stack exchange,提问作者Meowcolm024
相关产品推荐
相关产品推荐

