我正在玩van Laarhoven镜头并遇到一个问题,其中类型检查器拒绝了eta-reduced函数的类型:
{-# LANGUAGE RankNTypes #-}
import Control.Applicative
type Lens c a = forall f . Functor f => (a -> f a) -> (c -> f c)
getWith :: (a -> b) -> ((a -> Const b a) -> (c -> Const b c)) -> (c -> b)
getWith f l = getConst . l (Const . f)
get :: Lens c a -> c -> a
get lens = getWith id lens
以上类型检查,但如果我将get
缩减为
get :: Lens c a -> c -> a
get = getWith id
然后GHC(7.4.2)抱怨
Couldn't match expected type `Lens c a'
with actual type `(a0 -> Const b0 a0) -> c0 -> Const b0 c0'
Expected type: Lens c a -> c -> a
Actual type: ((a0 -> Const b0 a0) -> c0 -> Const b0 c0)
-> c0 -> b0
In the return type of a call of `getWith'
In the expression: getWith id
我可以理解,如果函数没有明确的类型签名,那么结合单同性限制的eta-reduction可能会混淆类型推断,特别是当我们处理更高级别的类型时,但在这种情况下我我不确定发生了什么。
是什么导致GHC拒绝eta减少形式,这是GHC中的错误/限制或更高等级类型的一些基本问题?
答案 0 :(得分:12)
我要说的原因不在于η减少本身,问题是RankNTypes
你失去principal types并输入类型。
高阶排序类型推断的问题是在推断遵守规则的λx.M
类型时
Γ, x:σ |- M:ρ
----------------------
Γ |- λx:σ.M : σ→ρ
我们不知道我们应该为x
选择什么类型的σ。在Hindley-Milner类型系统的情况下,我们将自己局限于x
的类型 - 量词 - 免费类型,并且推理是可能的,但不是任意排序类型。
所以即使使用RankNTypes
,当编译器遇到没有显式类型信息的术语时,它会转向Hindley-Milner并推断出它的rank-1主体类型。但是,在您的情况下,getWith id
所需的类型是rank-2,因此编译器无法自行推断它。
你的明确案例
get lens = getWith id lens
对应于x
的类型已明确指定λ(x:σ).Mx
的情况。在类型检查lens
之前,编译器知道getWith id lens
的类型。
在简化案例中
get = getWith id
编译器必须自己推断getWidth id
的类型,因此它坚持使用Hindley-Milner并推断出排名为1的类型。
答案 1 :(得分:5)
实际上它非常简单:GHC推断出每个表达式的类型,然后开始在=
之间统一它们。当只有rank-1类型时,这总是很好,因为选择了最多态的(这是明确定义的);所以任何可能的统一都会成功。
但即使可能,也不会选择更一般的排名2类型,因此getWith id
推断为((a -> Const a a) -> c -> Const a c) -> (c -> a)
而不是(forall f . Functor f => (a -> f a) -> c -> f c) -> (c -> a)
。我想如果GHC确实做了这样的事情,传统的1级类型推断就不再适用了。或者它永远不会终止,因为不存在一个明确定义的最多态的rank-n类型。
这并不能解释为什么它无法从get
的签名中看到需要在这里选择排名-2,但可能有充分的理由认为好。