是否可以通过GHC的泛型派生派生Data.Vector.Unbox?

时间:2019-06-27 11:28:06

标签: haskell template-haskell storable ghc-generics

可以通过GHC的通用派生机制:http://hackage.haskell.org/package/derive-storable(和https://hackage.haskell.org/package/derive-storable-plugin来获得性能)派生Storable。我可以找到唯一的派生Data.Vector.Unbox的库,它使用模板Haskell:http://hackage.haskell.org/package/vector-th-unbox。它还要求用户编写一些代码。这不是完全自动的。

我的问题是,deriving-storable是否也可以使用类似Unbox的库,还是由于UnboxStorable不同的一些基本方式而无法实现?如果是后者,那是否意味着也无法创建一个库,该库允许为任何Unbox类型自动派生Storable,因为我找不到这样的库。

我问,因为理想情况下,我想避免使用模板Haskell和使用vector-th-unbox所需的手动注释。

1 个答案:

答案 0 :(得分:3)

假设我们有一些Generic_类在我们自己的类型和一些恰好具有Unbox实例(相当于MVectorVector的统一表示形式之间进行转换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_实例,则可以采用此方法并从字面上替换{ {1}}和MyType

YourType

理论上,所有这些样板都可以使用内部语言功能(与TemplateHaskell或CPP相对)进行自动化。但是,在当前的事物状态中,还是有很多问题困扰着。

首先,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 本质上是GHC.Generics中的Generic_。但是,GHC派生的统一表示形式不是以元组Generic的形式出现,而是以某种特定类型的构造函数((,):+::*:,等),缺少M1个实例。

  • 可以添加此类Unbox实例以直接使用Unbox
  • generics-eot具有Generic的变体,它依赖于元组,在这里可以直接替换Generic

第二,Generic_MVector有很多方法。为了避免全部列出,可能希望利用Vector(或DerivingVia),但是它们并不适用,因为有几种防止强制的多态单子方法(例如{{1 }}。目前,我可以想到的最简单的抽象方法是CPP宏。实际上,矢量包在内部使用了该技术,并且它可能以某种方式可重用。我相信要正确解决这些问题,就需要对Vector / MVector架构进行深入的重新设计。

要点(不完整,但可编译):https://gist.github.com/Lysxia/c7bdcbba548ee019bf6b3f1e388bd660