简体   繁体   English

功能组成和forall'ed类型

[英]Function composition and forall'ed types

Let's say we have some code like this, which typechecks just fine: 假设我们有一些像这样的代码,哪些类型很好:

{-# 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"

We note that _pp x = _pa $ _pfa x is too verbose, and we try to replace it with _pp = _pa . _pfa 我们注意到_pp x = _pa $ _pfa x太冗长了,我们尝试用_pp = _pa . _pfa替换它_pp = _pa . _pfa _pp = _pa . _pfa . _pp = _pa . _pfa Suddenly the code doesn't typecheck anymore, failing with error messages similar to 突然,代码不再进行类型检查,失败的错误消息类似于

• Couldn't match type ‘Foo a0 -> m0 ()’ with ‘PA a’
  Expected type: (Foo a0 -> m0 ()) -> Foo a -> m ()
    Actual type: PA a -> A a

I guess this is due to m in the definition of type aliases being forall 'd — indeed, replacing m with some exact type fixes the issue. 我想这是因为类型别名的定义中的mforall - 实际上,用某种确切的类型替换m修复了这个问题。 But the question is: why does forall break things in this case? 但问题是:为什么forall在这种情况下破坏事情?

Bonus points for trying to figure out why replacing dummy recursive definitions of _pfa and _pa with usual _pfa = undefined results in GHC complaining about unification variables and impredicative polymorphism: 试图找出为什么用通常的_pfa = undefined替换_pfa_pa虚拟递归定义的_pfa导致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

Just to be clear, when you write: 只是要清楚,当你写:

_pa :: PA a -> A a

The compiler expands the type synonyms and then moves the quantifiers and constraints upward, like so: 编译器扩展类型同义词,然后向上移动量词和约束,如下所示:

_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 ()

So _pa has a rank-2 polymorphic type, because it has a forall nested to the left of a function arrow. 所以_pa有一个rank-2多态类型,因为它有一个嵌套在函数箭头左边的forall。 Same goes for _pfa . _pfa They expect polymorphic functions as arguments. 他们期望多态函数作为参数。

To answer the actual question, I'll first show you something strange. 要回答实际问题,我首先会向您展示一些奇怪的东西。 These both typecheck: 这些都是typecheck:

_pp :: PPFA a -> A a
_pp x = _pa $ _pfa x

_pp :: PPFA a -> A a
_pp x = _pa (_pfa x)

This, however, does not: 但是,这不是:

apply :: (a -> b) -> a -> b
apply f x = f x

_pp :: PPFA a -> A a
_pp x = apply _pa (_pfa x)

Unintuitive, right? 不直观,对吗? This is because the application operator ($) is special-cased in the compiler to allow instantiating its type variables with polymorphic types, in order to support runST $ do { … } rather than runST (do { … }) . 这是因为应用程序运算符($)在编译器中是特殊的,以允许使用多态类型实例化其类型变量,以便支持runST $ do { … }而不是runST (do { … })

Composition (.) , however, is not special-cased. 然而,组合(.)不是特殊的。 So when you call (.) on _pa and _pfa , it instantiates their types first. 所以当你在_pa_pfa上调用(.)时,它首先实例化它们的类型。 Thus you end up trying to pass the non-polymorphic result of _pfa , of the type (Foo a0 -> m0 ()) -> Foo a -> m () mentioned in your error message, to the function _pa , but it expects a polymorphic argument of type P a , so you get a unification error. 因此,您最终尝试将错误消息中提到的类型(Foo a0 -> m0 ()) -> Foo a -> m ()_pfa非多态结果传递给函数_pa ,但它期望类型为P a的多态参数,因此会出现统一错误。

undefined :: a doesn't typecheck because it tries to instantiate a with a polymorphic type, an instance of impredicative polymorphism. undefined :: a没有类型检查因为它试图用多态类型实例化a ,这是一个impregicative polymorphism的实例。 That's a hint as to what you should do—the standard way of hiding impredicativity is with a newtype wrapper: 这是一个提示,你应该做的,什么样的隐藏非直谓性的标准方法是用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 () }

Now this definition compiles without error: 现在这个定义编译没有错误:

_pp :: PPFA a -> A a
_pp = _pa . _pfa

With the cost that you need to explicitly wrap and unwrap to tell GHC when to abstract and instantiate: 使用显式包装和解包所需的成本来告诉GHC何时抽象和实例化:

_pa :: PA a -> A a
_pa x = A (unPA x)

Instantiating polymorphic type variables with polymorphic types is called impredicative polymorphism. 使用多态类型实例化多态类型变量称为impredicative polymorphism。 -- GHC user guide - GHC用户指南

As the error message indicates, GHC only allows a type variable to be instantiated with a monomorphic, rank 0 type. 如错误消息所示,GHC仅允许使用单态,秩0类型实例化类型变量。 My guess is that type checking with impredicative polymorphism is trickier to implement than it may seem. 我的猜测是,使用impregicative polymorphism进行类型检查比实际看起来更难实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM