不能将 '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
.
现在,您可能想知道为什么 ReaderT
有 nominal
第三个参数。要理解这一点,请考虑其定义:
newtype ReaderT r m a = ReaderT (r -> m a)
上面a
的作用应该是什么?这要看情况。如果 m :: * -> *
在其参数上是 representational
,那么 ReaderT
在 a
上也是如此。 nominal
和 phantom
也是如此。 "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
我有以下 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
.
现在,您可能想知道为什么 ReaderT
有 nominal
第三个参数。要理解这一点,请考虑其定义:
newtype ReaderT r m a = ReaderT (r -> m a)
上面a
的作用应该是什么?这要看情况。如果 m :: * -> *
在其参数上是 representational
,那么 ReaderT
在 a
上也是如此。 nominal
和 phantom
也是如此。 "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