如何使用GeneralizedNewTypeDeriving派生Vector和MVector实例?
修复Data.Vector.Unboxed中GeneralizedNewTypeDeriving派生实例的编译错误
问题背景
Data.Vector.Unboxed的Haddock文档提供了使用GeneralizedNewTypeDeriving自动派生Vector和MVector实例的示例,但添加必要编译指令后,代码在GHC 9.0.2(对应stack resolver lts-19.33)下无法通过类型检查,报错核心是coerce无法匹配m Int和m Foo的类型表示,原因是GHC无法确定monad参数m的角色。
原示例代码:
{-# LANGUAGE GeneralizedNewTypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-} import qualified Data.Vector.Generic as G import qualified Data.Vector.Generic.Mutable as M import qualified Data.Vector.Unboxed as U newtype Foo = Foo Int newtype instance U.MVector s Foo = MV_Int (U.MVector s Int) newtype instance U.Vector Foo = V_Int (U.Vector Int) deriving instance M.MVector U.MVector Foo deriving instance G.Vector U.Vector Foo instance U.Unbox Foo
报错信息片段:
• Couldn't match representation of type: m Int with that of: m Foo arising from a use of ‘GHC.Prim.coerce’ NB: We cannot know what roles the parameters to ‘m’ have; we must assume that the role is nominal • In the expression: GHC.Prim.coerce @(U.MVector (Control.Monad.Primitive.PrimState m) Int -> Int -> m Int) @(U.MVector (Control.Monad.Primitive.PrimState m) Foo -> Int -> m Foo) (M.basicUnsafeRead @U.MVector @Int)
用户已通过Data.Vector.Unboxed.Deriving的derivingUnbox实现需求,但希望使用GeneralizedNewTypeDeriving避免手动处理构造函数:
derivingUnbox "Foo" [t|Foo -> Int|] [|\(Foo i) -> i|] [|Foo|]
问题原因
GHC 9.0+对多参数类型类的角色推断更严格:
M.MVector和G.Vector属于多参数类型类,其元素类型参数默认被推断为nominal角色(即类型必须完全一致,不允许通过coerce跨类型转换)。GeneralizedNewTypeDeriving依赖coerce来自动派生实例,但GHC无法确定monadm的参数角色,因此拒绝执行跨类型的coerce操作。
修复方案
添加RoleAnnotations语言扩展,显式标注U.MVector和U.Vector的元素类型参数为representational角色(允许基于底层表示的coerce转换),修改后的代码如下:
{-# LANGUAGE GeneralizedNewTypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RoleAnnotations #-} -- 新增扩展 import qualified Data.Vector.Generic as G import qualified Data.Vector.Generic.Mutable as M import qualified Data.Vector.Unboxed as U newtype Foo = Foo Int -- 标注U.MVector的第二个参数(元素类型)为representational type role U.MVector nominal representational newtype instance U.MVector s Foo = MV_Int (U.MVector s Int) -- 标注U.Vector的参数(元素类型)为representational type role U.Vector representational newtype instance U.Vector Foo = V_Int (U.Vector Int) deriving instance M.MVector U.MVector Foo deriving instance G.Vector U.Vector Foo instance U.Unbox Foo
说明
RoleAnnotations允许我们手动指定类型构造器参数的角色,这里将元素类型参数设为representational,告诉GHC:只要两个类型的底层表示相同(比如Foo和Int作为newtype),就可以安全地通过coerce转换。- 此修复保留了
GeneralizedNewTypeDeriving的便利性,无需手动编写构造函数的转换逻辑。
内容的提问来源于stack exchange,提问作者thisisrandy
相关产品推荐
相关产品推荐

