使用复杂类型参数强制记录时出现问题
Problem coercing a record with complex type parameters
我有这个记录:
import Data.Functor.Identity
import Control.Monad.Trans.Identity
import Data.Coerce
data Env m = Env {
logger :: String -> m ()
}
env :: Env IO
env = undefined
和这个强制转换函数
decorate
:: Coercible (r_ m) (r_ (IdentityT m))
=> r_ m -> r_ (IdentityT m)
decorate = coerce
适用于没有问题的记录值:
decoratedEnv :: Env (IdentityT IO)
decoratedEnv = decorate env
但是,如果我定义唯一稍微复杂一点的记录
data Env' h m = Env' {
logger' :: h (String -> m ())
}
env' :: Env' Identity IO
env' = undefined
并尝试像我之前那样插入一个 IdentityT
包装器
decoratedEnv' :: Env' Identity (IdentityT IO)
decoratedEnv' = decorate env'
我收到错误:
* Couldn't match type `IO' with `IdentityT IO'
arising from a use of `decorate'
在我看来,Env'
使用的额外 Identity
参数不应该阻止 coerce
工作。为什么 coerce
在这种情况下会失败?有没有办法让 coerce
工作?
强制 Coercible A B
并不意味着对所有 h
.Coercion (h A) (h B)
强制 Coercion (h A) (h B)
。
考虑这个 GADT:
data T a where
K1 :: Int -> T A
K2 :: Bool -> T B
将 T A
强制转换为 T B
等同于将 Int
转换为 Bool
,这显然不应该发生。
只有当我们知道 h
的参数具有正确的 role(例如 representational
或 phantom
,而不是 nominal
).
现在,在您的特定情况下,h
是已知的 (Identity
),并且肯定具有正确的 角色 ,因此它应该可以工作。我猜 GHC 强制系统没有那么强大,无法处理这样的“乖”h
而拒绝不乖的,所以它保守地拒绝一切。
添加一个类型孔似乎证实了这一点。我试过了
decoratedEnv' :: Env' Identity (IdentityT IO)
decoratedEnv' = _ decorate' @(Env' Identity)
出现错误
* Couldn't match representation of type: r_0 m0
with that of: r_0 (IdentityT m0)
arising from a use of decorate'
NB: We cannot know what roles the parameters to `r_0' have;
we must assume that the role is nominal
* In the first argument of `_', namely decorate'
In the expression: _ decorate' @(Env' Identity)
In an equation for decoratedEnv':
decoratedEnv' = _ decorate' @(Env' Identity)
“注意:”部分就在现场。
我也努力坚持,强行发挥作用
type role Env' representational representational
data Env' h m = Env' {
logger' :: h (String -> m ())
}
无济于事:
* Role mismatch on variable m:
Annotation says representational but role nominal is required
* while checking a role annotation for Env'
我能找到的最佳解决方法是 unwrap/rewrap,并利用 QuantifiedConstraints
本质上要求 h
具有非 nominal
角色:
decorate'
:: (forall a b. Coercible a b => Coercible (h a) (h' b))
=> Coercible m m'
=> Env' h m -> Env' h' m'
decorate' (Env' x) = Env' (coerce x)
decoratedEnv' :: Env' Identity (IdentityT IO)
decoratedEnv' = decorate' env'
这不是一个理想的解决方案,因为它违背了 Coercible
的精神。在这种情况下,重新包装只有 O(1) 的成本,但是如果我们有一个 Env' Identity IO
的列表要转换,我们将支付 O(N).
我有这个记录:
import Data.Functor.Identity
import Control.Monad.Trans.Identity
import Data.Coerce
data Env m = Env {
logger :: String -> m ()
}
env :: Env IO
env = undefined
和这个强制转换函数
decorate
:: Coercible (r_ m) (r_ (IdentityT m))
=> r_ m -> r_ (IdentityT m)
decorate = coerce
适用于没有问题的记录值:
decoratedEnv :: Env (IdentityT IO)
decoratedEnv = decorate env
但是,如果我定义唯一稍微复杂一点的记录
data Env' h m = Env' {
logger' :: h (String -> m ())
}
env' :: Env' Identity IO
env' = undefined
并尝试像我之前那样插入一个 IdentityT
包装器
decoratedEnv' :: Env' Identity (IdentityT IO)
decoratedEnv' = decorate env'
我收到错误:
* Couldn't match type `IO' with `IdentityT IO'
arising from a use of `decorate'
在我看来,Env'
使用的额外 Identity
参数不应该阻止 coerce
工作。为什么 coerce
在这种情况下会失败?有没有办法让 coerce
工作?
强制 Coercible A B
并不意味着对所有 h
.Coercion (h A) (h B)
强制 Coercion (h A) (h B)
。
考虑这个 GADT:
data T a where
K1 :: Int -> T A
K2 :: Bool -> T B
将 T A
强制转换为 T B
等同于将 Int
转换为 Bool
,这显然不应该发生。
只有当我们知道 h
的参数具有正确的 role(例如 representational
或 phantom
,而不是 nominal
).
现在,在您的特定情况下,h
是已知的 (Identity
),并且肯定具有正确的 角色 ,因此它应该可以工作。我猜 GHC 强制系统没有那么强大,无法处理这样的“乖”h
而拒绝不乖的,所以它保守地拒绝一切。
添加一个类型孔似乎证实了这一点。我试过了
decoratedEnv' :: Env' Identity (IdentityT IO)
decoratedEnv' = _ decorate' @(Env' Identity)
出现错误
* Couldn't match representation of type: r_0 m0
with that of: r_0 (IdentityT m0)
arising from a use of decorate'
NB: We cannot know what roles the parameters to `r_0' have;
we must assume that the role is nominal
* In the first argument of `_', namely decorate'
In the expression: _ decorate' @(Env' Identity)
In an equation for decoratedEnv':
decoratedEnv' = _ decorate' @(Env' Identity)
“注意:”部分就在现场。
我也努力坚持,强行发挥作用
type role Env' representational representational
data Env' h m = Env' {
logger' :: h (String -> m ())
}
无济于事:
* Role mismatch on variable m:
Annotation says representational but role nominal is required
* while checking a role annotation for Env'
我能找到的最佳解决方法是 unwrap/rewrap,并利用 QuantifiedConstraints
本质上要求 h
具有非 nominal
角色:
decorate'
:: (forall a b. Coercible a b => Coercible (h a) (h' b))
=> Coercible m m'
=> Env' h m -> Env' h' m'
decorate' (Env' x) = Env' (coerce x)
decoratedEnv' :: Env' Identity (IdentityT IO)
decoratedEnv' = decorate' env'
这不是一个理想的解决方案,因为它违背了 Coercible
的精神。在这种情况下,重新包装只有 O(1) 的成本,但是如果我们有一个 Env' Identity IO
的列表要转换,我们将支付 O(N).