我们假设我们有一些这样的代码,这样可以很好地解决这个问题:
{-# LANGUAGE RankNTypes #-}
data Foo a
type A a = forall m. Monad m => Foo a -> m ()
type PA a = forall m. Monad m => Foo a -> m ()
type PPFA a = forall m. Monad m => Foo a -> m ()
_pfa :: PPFA a -> PA a
_pfa = _pfa
_pa :: PA a -> A a
_pa = _pa
_pp :: PPFA a -> A a
_pp x = _pa $ _pfa x
main :: IO ()
main = putStrLn "yay"
我们注意到_pp x = _pa $ _pfa x
过于冗长,我们尝试将其替换为_pp = _pa . _pfa
。突然,代码不再进行类型检查,失败的错误消息类似于
• Couldn't match type ‘Foo a0 -> m0 ()’ with ‘PA a’
Expected type: (Foo a0 -> m0 ()) -> Foo a -> m ()
Actual type: PA a -> A a
我想这是因为类型别名的定义中m
为forall
' d - 实际上,用某种确切类型替换m
可以解决问题。但问题是:为什么forall
会破坏这种情况?
试图找出为什么用通常的_pfa
替换_pa
和_pfa = undefined
的虚拟递归定义的原因导致GHC抱怨统一变量和不可预测的多态性的加分点:
• Cannot instantiate unification variable ‘a0’
with a type involving foralls: PPFA a -> Foo a -> m ()
GHC doesn't yet support impredicative polymorphism
• In the expression: undefined
In an equation for ‘_pfa’: _pfa = undefined
答案 0 :(得分:5)
只是要明确,当你写:
_pa :: PA a -> A a
编译器扩展类型同义词,然后向上移动量词和约束,如下所示:
_pa
:: forall a.
(forall m1. Monad m1 => Foo a -> m1 ())
-> (forall m2. Monad m2 => Foo a -> m2 ())
_pa
:: forall m2 a. (Monad m2)
=> (forall m1. Monad m1 => Foo a -> m1 ())
-> Foo a -> m2 ()
所以_pa
具有rank-2多态类型,因为它有一个嵌套在函数箭头左侧的forall。同样适用于_pfa
。他们期望多态函数作为参数。
要回答实际问题,我首先会向您展示一些奇怪的东西。这些都是类型检查:
_pp :: PPFA a -> A a
_pp x = _pa $ _pfa x
_pp :: PPFA a -> A a
_pp x = _pa (_pfa x)
然而,这不是:
apply :: (a -> b) -> a -> b
apply f x = f x
_pp :: PPFA a -> A a
_pp x = apply _pa (_pfa x)
不直观,对吗?这是因为应用程序运算符($)
在编译器中是特殊的,以允许使用多态类型实例化其类型变量,以支持runST $ do { … }
而不是runST (do { … })
。
然而,作文(.)
不是特殊的。因此,当您在(.)
和_pa
上调用_pfa
时,它会首先实例化其类型。因此,您最终会尝试将错误消息中提到的_pfa
类型的(Foo a0 -> m0 ()) -> Foo a -> m ()
的非多态结果传递给函数_pa
,但是它期望类型为P a
的多态参数,因此会出现统一错误。
undefined :: a
没有进行类型检查,因为它试图用多态类型(一种不可预测的多态性实例)实例化a
。这是一个暗示你应该做什么 - 隐藏impredicativity的标准方法是使用newtype
包装器:
newtype A a = A { unA :: forall m. Monad m => Foo a -> m () }
newtype PA a = PA { unPA :: forall m. Monad m => Foo a -> m () }
newtype PPFA a = PPFA { unPPFA :: forall m. Monad m => Foo a -> m () }
现在这个定义编译没有错误:
_pp :: PPFA a -> A a
_pp = _pa . _pfa
使用显式包装和展开所需的成本告诉GHC何时抽象和实例化:
_pa :: PA a -> A a
_pa x = A (unPA x)
答案 1 :(得分:2)
使用多态类型实例化多态类型变量称为impredicative polymorphism。 - https://keras.io/preprocessing/image/#imagedatagenerator
如错误消息所示,GHC仅允许使用单形,等级0类型实例化类型变量。我的猜测是,使用impredicative polymorphism进行类型检查比实际看起来更难实现。