使用幻像参数强制类型类实例
Coercion of typeclass instances with phantom parameter
我有
{-# LANGUAGE RankNTypes, TypeInType #-}
import Data.Coerce
import Data.Kind
newtype IFix f i = IFix { unIFix :: f (IFix f) i }
class IFunctor (f :: (i -> Type) -> i -> Type) where
imap :: (forall i'. a i' -> b i') -> (forall i'. f a i' -> f b i')
f :: IFunctor f => forall i. IFix f i -> IFix f i
f = undefined
g :: IFunctor f => forall i. IFix f i -> IFix f i
g = IFix . imap f . unIFix
h :: IFunctor f => forall i. IFix f i -> IFix f i
h = coerce . imap f . coerce
i :: IFunctor f => forall i. IFix f i -> IFix f i
i = coerce (imap f)
其中 IFix
是索引类型构造函数的类型级固定点,i
是索引(一个幻影参数),IFunctor
是此类构造函数的 class索引类型构造函数实际上是函子,f
只是一个随机函数,g
、h
、i
尝试在 [=12= 下传播 f
] 包装纸。旁注:这是一个简化的示例,实际上我经常 运行 在其他设置中遇到类似的问题,手动重新包装变得有点乏味(对于一个我想避免将 unwrap-rewrap 函数映射到列表或其他结构).
input:18:5: error:
• Couldn't match representation of type ‘f (IFix f) i’
with that of ‘f0 (IFix f1) i0’
arising from a use of ‘coerce’
|
18 | h = coerce . imap f . coerce
| ^^^^^^
input:21:5: error:
• Couldn't match representation of type ‘f (IFix f) i’
with that of ‘f2 (IFix f3) i1’
arising from a use of ‘coerce’
|
21 | i = coerce (imap f)
| ^^^^^^^^^^^^^^^
老实说,我并不感到惊讶,但由于我没有看到强制转换的幕后情况,所以我想知道,是否有办法修改我的定义,以便可以应用 coerce
?我试过 RoleAnnotations
但
type role IFix nominal phantom
无效
- 我不知道如何为类型 class 要求类型角色(而且我担心没有办法,因为文档提到类型classes 假设参数是名义上的)
所以我的问题是,有没有一种方法可以使强制在这种情况下起作用,如果没有,是否有任何深层原因,或者仅仅是当前角色推理实现的局限性。我天真的观点使我相信类型 class 可以对参数的角色施加约束,并且实例必须满足约束。是否有关于一些有用的强制技巧的好资源?
实际上,g
和 h
不进行类型检查,因为 imap f
的类型不明确。
imap :: IFunctor f => (a ~> b) -> (f a ~> f b)
例如,在 h
、imap f
中推断出以下类型,具有自由统一变量 ?f0
、?f1
、?i0
:
h = coerce . (imap f :: ?f0 (IFix ?f1) ?i0 -> ?f0 (IFix ?f1) ?i0) . coerce
通常上下文允许我们实例化这些变量,但这里的上下文是:
coerce . _ . coerce
注意 coerce :: Coercible a b => a -> b
的类型,就类型推断而言,它完全解耦了输入和输出类型。
我们可以使用扩展名ScopedTypeVariables
来注释imap f
:
h :: forall f i. IFunctor f => IFix f i -> IFix f i
h = coerce . (imap f :: f (IFix f) i -> f (IFix f) i) . coerce
或特化coerce
以限制其形状:
type E a = a -> a
i :: IFunctor f => IFix f i -> IFix f i
i = (coerce :: E (f (IFix f) i) -> E (IFix f i)) (imap f)
coerce
本身太笼统了,所以通常必须添加这样的注释。拥有一个常见专业化的图书馆可能是值得的。
我有
{-# LANGUAGE RankNTypes, TypeInType #-}
import Data.Coerce
import Data.Kind
newtype IFix f i = IFix { unIFix :: f (IFix f) i }
class IFunctor (f :: (i -> Type) -> i -> Type) where
imap :: (forall i'. a i' -> b i') -> (forall i'. f a i' -> f b i')
f :: IFunctor f => forall i. IFix f i -> IFix f i
f = undefined
g :: IFunctor f => forall i. IFix f i -> IFix f i
g = IFix . imap f . unIFix
h :: IFunctor f => forall i. IFix f i -> IFix f i
h = coerce . imap f . coerce
i :: IFunctor f => forall i. IFix f i -> IFix f i
i = coerce (imap f)
其中 IFix
是索引类型构造函数的类型级固定点,i
是索引(一个幻影参数),IFunctor
是此类构造函数的 class索引类型构造函数实际上是函子,f
只是一个随机函数,g
、h
、i
尝试在 [=12= 下传播 f
] 包装纸。旁注:这是一个简化的示例,实际上我经常 运行 在其他设置中遇到类似的问题,手动重新包装变得有点乏味(对于一个我想避免将 unwrap-rewrap 函数映射到列表或其他结构).
input:18:5: error:
• Couldn't match representation of type ‘f (IFix f) i’
with that of ‘f0 (IFix f1) i0’
arising from a use of ‘coerce’
|
18 | h = coerce . imap f . coerce
| ^^^^^^
input:21:5: error:
• Couldn't match representation of type ‘f (IFix f) i’
with that of ‘f2 (IFix f3) i1’
arising from a use of ‘coerce’
|
21 | i = coerce (imap f)
| ^^^^^^^^^^^^^^^
老实说,我并不感到惊讶,但由于我没有看到强制转换的幕后情况,所以我想知道,是否有办法修改我的定义,以便可以应用 coerce
?我试过 RoleAnnotations
但
type role IFix nominal phantom
无效- 我不知道如何为类型 class 要求类型角色(而且我担心没有办法,因为文档提到类型classes 假设参数是名义上的)
所以我的问题是,有没有一种方法可以使强制在这种情况下起作用,如果没有,是否有任何深层原因,或者仅仅是当前角色推理实现的局限性。我天真的观点使我相信类型 class 可以对参数的角色施加约束,并且实例必须满足约束。是否有关于一些有用的强制技巧的好资源?
实际上,g
和 h
不进行类型检查,因为 imap f
的类型不明确。
imap :: IFunctor f => (a ~> b) -> (f a ~> f b)
例如,在 h
、imap f
中推断出以下类型,具有自由统一变量 ?f0
、?f1
、?i0
:
h = coerce . (imap f :: ?f0 (IFix ?f1) ?i0 -> ?f0 (IFix ?f1) ?i0) . coerce
通常上下文允许我们实例化这些变量,但这里的上下文是:
coerce . _ . coerce
注意 coerce :: Coercible a b => a -> b
的类型,就类型推断而言,它完全解耦了输入和输出类型。
我们可以使用扩展名ScopedTypeVariables
来注释imap f
:
h :: forall f i. IFunctor f => IFix f i -> IFix f i
h = coerce . (imap f :: f (IFix f) i -> f (IFix f) i) . coerce
或特化coerce
以限制其形状:
type E a = a -> a
i :: IFunctor f => IFix f i -> IFix f i
i = (coerce :: E (f (IFix f) i) -> E (IFix f i)) (imap f)
coerce
本身太笼统了,所以通常必须添加这样的注释。拥有一个常见专业化的图书馆可能是值得的。