假设我们有一些Generic_
类可以在我们自己的类型和一些恰好有一个Unbox
实例的统一表示之间进行转换(这相当于变体MVector
的Vector
实例Unboxed
):
class Generic_ a where
type Rep_ (a :: Type) :: Type
to_ :: a -> Rep_ a
from_ :: Rep_ a -> a
然后我们可以使用它来获得MVector
/方法的通用实现Vector
:
-- (auxiliary definitions of CMV and uncoercemv at the end of this block)
-- vector imports (see gist at the end for a compilable sample)
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as UM
import Data.Vector.Generic.Mutable.Base (MVector(..))
-- MVector
gbasicLength :: forall a s. CMV s a => UM.MVector s a -> Int
gbasicLength = basicLength @UM.MVector @(Rep_ a) @s . coerce
gbasicUnsafeSlice :: forall a s. CMV s a => Int -> Int -> UM.MVector s a -> UM.MVector s a
gbasicUnsafeSlice i j = uncoercemv . basicUnsafeSlice @UM.MVector @(Rep_ a) @s i j . coerce
-- etc.
-- idem Vector
-- This constraints holds when the UM.MVector data instance of a is
-- representationally equivalent to the data instance of its generic
-- representation (Rep_ a).
type CMV s a = (Coercible (UM.MVector s a) (UM.MVector s (Rep_ a)), MVector UM.MVector (Rep_ a))
-- Sadly coerce doesn't seem to want to solve this correctly so we use
-- unsafeCoerce as a workaround.
uncoercemv :: CMV s a => UM.MVector s (Rep_ a) -> UM.MVector s a
uncoercemv = unsafeCoerce
现在,如果我们有一些泛型类型
data MyType = MyCons Int Bool ()
我们可以定义一个与元组同构的通用实例
instance Generic_ MyType where
type Rep_ MyType = (Int, Bool, ())
to_ (MyCons a b c) = (a, b, c)
from_ (a, b, c) = MyCons a b c
从那里开始,有一个完全通用的方法来获取它的Unbox
实例,如果你有YourType
它自己的Generic_
实例,你可以把它拿来替换MyType
为YourType
.
newtype instance UM.MVector s MyType
= MVMyType { unMVMyType :: UM.MVector s (Rep_ MyType) }
instance MVector UM.MVector MyType where
basicLength = gbasicLength
basicUnsafeSlice = gbasicUnsafeSlice
-- etc.
-- idem (Vector U.Vector MyType)
-- MVector U.Vector & Vector UM.MVector = Unbox
instance Unbox MyType
理论上,所有这些样板文件都可以通过内部语言特性自动化(与 TemplateHaskell 或 CPP 不同)。但是,在当前的情况下,存在各种问题。
首先,Generic_
本质上Generic
来自GHC.Generics
。然而,由 GHC 派生的统一表示不是元组,而是缺乏实例(,)
的特定类型构造函数(:+:
、:*:
、M1
等) 。Unbox
- 这样
Unbox
的实例可以添加Generic
直接使用
- generics-eot有一个
Generic
依赖元组的变体,可以直接替代这里Generic_
。
第二,MVector
有Vector
相当多的方法。为了避免必须将它们全部列出,人们可能希望利用DerivingVia
(或GeneralizedNewtypeDeriving
),但是它们不适用,因为有一些多态一元方法可以防止强制转换(例如,basicUnsafeNew
)。目前,我能想到的最简单的抽象方法是 CPP 宏。事实上,vector 包在内部使用了该技术,并且它可能以某种方式可重用。我相信正确解决这些问题需要对 Vector/MVector 架构进行深入的重新设计。
要点(不完整,但可编译):https ://gist.github.com/Lysxia/c7bdcbba548ee019bf6b3f1e388bd660