不能将 'coerce' 数据类型作为字段 'Reader'

Cannot 'coerce' data type with 'Reader' as a field

我有以下 Haskell 代码,可以完美编译:

import Control.Monad.Reader (Reader (..))
import Data.Coerce (Coercible, coerce)

data Flow i o = Flow (i -> o) (o -> i)

coerceFlow
    :: (Coercible i i', Coercible o o')
    => Flow i o
    -> Flow i' o'
coerceFlow = coerce

但是,如果我将 Flow 类型的定义更改为以下内容:

data Flow i o = Flow (i -> Reader Int o) (o -> i)

我开始看到一个奇怪的错误:

Coerce.hs:10:14: error:
    • Couldn't match type ‘o’ with ‘o'’ arising from a use of ‘coerce’
      ‘o’ is a rigid type variable bound by
        the type signature for:
          coerceFlow :: forall i i' o o'.
                        (Coercible i i', Coercible o o') =>
                        Flow i o -> Flow i' o'
        at Coerce.hs:(6,1)-(9,17)
      ‘o'’ is a rigid type variable bound by
        the type signature for:
          coerceFlow :: forall i i' o o'.
                        (Coercible i i', Coercible o o') =>
                        Flow i o -> Flow i' o'
        at Coerce.hs:(6,1)-(9,17)
    • In the expression: coerce
      In an equation for ‘coerceFlow’: coerceFlow = coerce
    • Relevant bindings include
        coerceFlow :: Flow i o -> Flow i' o' (bound at Coerce.hs:10:1)
   |
10 | coerceFlow = coerce
   |              ^^^^^^

据我了解,我的数据类型不再自动 Coercible。有没有办法告诉 GHC 我可以自动强制类型 Flow 的值?我可以手动 coerce 每个字段,但我想一次 coerce 整个数据类型(这是 DerivingVia 工作所必需的)。

我试过像这样使用 RoleAnnotations 扩展:

type role Flow representational representational

但是我看到一个错误:

Coerce.hs:6:1: error:
    • Role mismatch on variable o:
        Annotation says representational but role nominal is required
    • while checking a role annotation for ‘Flow’
  |
6 | type role Flow representational representational
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

让我们调查一下:

> :info Reader
type Reader r = ReaderT r Data.Functor.Identity.Identity :: * -> *
        -- Defined in `Control.Monad.Trans.Reader'

因此,Reader 是根据 ReaderT 定义的。

> :info ReaderT
type role ReaderT representational representational nominal
newtype ReaderT r (m :: k -> *) (a :: k)
  = ReaderT {runReaderT :: r -> m a}
        -- Defined in `Control.Monad.Trans.Reader'

... 并且 ReaderT 在其第三个参数中为 nominal,导致 Reader 在其第二个参数中为 nominal,并使强制转换失败。你不能使用你的 Flow 类型的角色注释来颠覆它,因为那会处理之前的角色注释 ReaderT.

现在,您可能想知道为什么 ReaderTnominal 第三个参数。要理解这一点,请考虑其定义:

newtype ReaderT r m a = ReaderT (r -> m a)

上面a的作用应该是什么?这要看情况。如果 m :: * -> * 在其参数上是 representational,那么 ReaderTa 上也是如此。 nominalphantom 也是如此。 "best" 在这里表达角色的方式是使用像

这样的角色多态性
type role forall r .
     ReaderT representational (representational :: (* with role r) -> *) r

其中第三个参数的作用取决于第二个更高级的参数。

唉,GHC 不像上面那样支持角色多态性,所以我们只能使用最受限制的角色:nominal.

@chi 解释的问题不再是问题,如果你避免使用 ReaderT 并使用教科书 (->) r monad:

{-# LANGUAGE FlexibleContexts #-}
module Foo where
import Data.Coerce (Coercible, coerce)

newtype RT r o = RT { runR :: r -> o }
data Flow i o = Flow (i -> RT Int o) (o -> i)

coerceFlow
    :: (Coercible i i', Coercible o o')
    => Flow i o
    -> Flow i' o'
coerceFlow = coerce