自动派生具有关联类型同义词的Data.Vector.Unbox

cro*_*eea 6 haskell type-families template-haskell deriving

我有一个数据类型

newtype Zq q = Zq (IntType q)
Run Code Online (Sandbox Code Playgroud)

其中'q'将是该类的一个实例

class Foo a where
   type IntType a
Run Code Online (Sandbox Code Playgroud)

'IntType'只是与'q'相关的基础表示(即Int,Integral等).

我想让Zq成为Data.Vector.Unbox的一个实例.我们目前正在使用大约50行简单代码手动派生Unbox,如上面的链接所示.我们将在代码中创建几种不同类型的"Unbox",因此为每种类型写入50行并不吸引人.

我在这里找到了两个选择.一种替代方法是使用此包使用Template Haskell来派生Unbox的实例.TH代码看起来像:

derivingUnbox "Zq"
  [d| instance (Foo q, U.Unbox (IntType q)) => Unbox' (ZqBasic q) (IntType q) |]
  [| \ (Zq x) -> x |]
  [| \ x -> Zq x |]
Run Code Online (Sandbox Code Playgroud)

问题是,我不能使用关联的类型同义词定义实例(或者我可以??)

[一个相关问题:为什么TypeSynonymInstances是FlexibleInstances所暗示的扩展,不允许关联类型的同义词实例?这在某种程度上是一个根本上不同的野兽吗?]

我目前解决这个问题的方法是将Zq重新定义为

newtype Zq q i = Zq i
Run Code Online (Sandbox Code Playgroud)

然后添加等式约束

i~(IntType q)
Run Code Online (Sandbox Code Playgroud)

在每个涉及(Zq qi)的实例中,这不是很优雅.我的(工作)Unbox派生成了

derivingUnbox "Zq"
  [d| instance (U.Unbox i, i~IntType q, Foo q) => Unbox' (Zq q i) i |]
  [| \ (Zq x) -> x |]
  [| \ x -> Zq x |]
Run Code Online (Sandbox Code Playgroud)

我觉得我应该能够实现这一目标而不必明确暴露出类型'i'.我所做的就是将它从关联类型同义词移动到具有相等约束的显式参数.为什么这种"从根本上"是一种不同的(显然更安全)的方法?有什么方法可以避免添加类型参数'i'并仍然获得自动Unbox派生?

除了额外的类型参数,我在使用TH包导出Unbox for(Vector r)时遇到了麻烦,也就是说,我想制作Unbox Vector的Unbox Vector.我的尝试是这样的:

newtype Bar r = Bar (Vector r)

derivingUnbox "Bar"
  [d| instance (Unbox r) => Unbox' (Bar r) (Vector r) |]
  [| \ (Bar x) -> x |]
  [| \ x -> Bar x |]
Run Code Online (Sandbox Code Playgroud)

但我得到(很多)错误,如:

`basicUnsafeFreeze` is not a (visible) method of class `Data.Vector.Generic.Base.Vector`
Run Code Online (Sandbox Code Playgroud)

我不知道为什么它找不到这个方法,当它对我的Zq类型工作得很好.


上面列出的第二种方法是使用扩展名GeneralizedNewtypeDeriving.我用这种方法看到的最大问题是我有一些实际的数据(而不是Newtype),我需要成为Unbox.但是,只使用扩展名,我应该可以写

newtype Zq q = Zq (IntType q) deriving (Unbox, M.MVector MVector, G.Vector Vector)
Run Code Online (Sandbox Code Playgroud)

或至少

newtype Zq q i = Zq i deriving (Unbox, M.MVector MVector, G.Vector Vector)
Run Code Online (Sandbox Code Playgroud)

第一个导致错误:

No instance for (Unbox (IntType q)) arising from the `deriving` clause of a data type declaration
No instance for (M.MVector MVector (IntType q)) ""
No instance for (G.Vector Vector (IntType q)) ""
Run Code Online (Sandbox Code Playgroud)

第二个给出:

No instance for (M.MVector MVector i) ""
No instance for (G.Vector U.Vector i) ""
Run Code Online (Sandbox Code Playgroud)

我不确定为什么它不能导出这些实例,因为上面的帖子让我相信它应该能够.也许我可以使用与GeneralizedNewtypeDeriving相关联的类型同义词来逃避?(当我需要为'数据'派生Unbox时,这仍然(可能)无法解决我的问题.)

谢谢你的帮助!

rei*_*erp 4

您在这里遇到了几个单独的问题:

TH 方法

是的,关联类型同义词的类实例是非法的

确实,您无法为关联的类型同义词或类型函数定义类实例,这是有充分理由的:编译器无法判断它们是否重叠。例如:

type family F a
instance Eq (F Int)
instance Eq (F Bool)
Run Code Online (Sandbox Code Playgroud)

这些实例是否重叠?鉴于上面的源代码,我们无法判断:这取决于后来某人如何定义F. 例如,他们可以定义

type instance F Int = Double
type instance F Bool = Double
Run Code Online (Sandbox Code Playgroud)

那么 的两个实例Eq实际上是重叠的。

vector-th-unbox您遇到了软件包问题

如果您查看Unbox所需的实际实例,您实际上并不需要IntType q;的实例。你想要的只是这样:

instance (Unbox (IntType q), Foo q) => Unbox (Zq q) where
    ...
Run Code Online (Sandbox Code Playgroud)

问题在于,该vector-th-unbox包强制您使用假类型类Unbox'来传达中间表示类型(IntType q在您的情况下),作为滥用 Template Haskell 语法来传递类型的便捷方法。然后 GHC 看到你写了信Unbox' (Zq q) (IntType q)并抱怨。我建议为该vector-th-unbox包提交一个错误。

Unbox为了Vector r

我认为路易斯·沃瑟曼(Louis Wasserman)对此进行了报道。

GeneralizedNewtypeDeriving方法

您遇到的特定编译错误是因为 GHC 无法推断适当的上下文。对于与您遇到的问题类似的大多数问题,StandaloneDeriving语言扩展可以解决您的问题:

deriving instance Unbox (IntType q) => Unbox (Zq q)
deriving instance Unbox (IntType q) => M.MVector MVector (Zq q)
deriving instance Unbox (IntType q) => G.Vector Vector (Zq q)
Run Code Online (Sandbox Code Playgroud)

但不要这样做!

虽然GeneralizedNewtypeDeriving通常完全按照您的意愿行事,但它在某些基本方面被破坏了,并且Unbox它产生的实例完全被破坏了!因此,在游说 Liyang 解决您当前的问题之后,请坚持使用 TH 方法。