7

我有一个数据类型

newtype Zq q = Zq (IntType q)

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

class Foo a where
   type IntType a

而“IntType”只是与“q”相关的底层表示(即 Int、Integral 等)。

我想让 Zq 成为Data.Vector.Unbox的一个实例。我们目前正在使用上面链接中建议的大约 50 行简单代码手动派生 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 |]

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

[一个相关问题:为什么TypeSynonymInstances是 Fl​​exibleInstances隐含的扩展,不允许关联类型同义词实例?这是某种根本不同的野兽吗?]

我目前对该问题的解决方案是将 Zq 重新定义为

newtype Zq q i = Zq i

然后添加等式约束

i~(IntType q)

在每个涉及 (Zq qi) 的情况下,这不是很优雅。我的(工作)拆箱派生变为

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

我觉得我应该能够在不诉诸于显式暴露类型“i”的情况下完成此操作。我所做的只是将它从关联的类型同义词移动到具有等式约束的显式参数。为什么这种“从根本上”是一种不同(而且显然更安全)的方法?有什么方法可以避免添加类型参数“i”并仍然获得自动拆箱派生?

除了额外的类型参数,我在使用 TH 包为 (Vector r) 派生 Unbox 时遇到了问题,也就是说,我想制作一个 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 |]

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

`basicUnsafeFreeze` is not a (visible) method of class `Data.Vector.Generic.Base.Vector`

我不确定为什么它找不到这个方法,当它适用于我的 Zq 类型时。


上面列出的第二种方法是使用扩展 GeneralizedNewtypeDeriving。我看到这种方法的最大问题是我有一些需要拆箱的实际数据(而不是新类型)。但是,仅使用扩展名,我应该可以编写

newtype Zq q = Zq (IntType q) deriving (Unbox, M.MVector MVector, G.Vector Vector)

或者至少

newtype Zq q i = Zq i deriving (Unbox, M.MVector MVector, G.Vector Vector)

第一个导致错误:

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)) ""

第二个给出:

No instance for (M.MVector MVector i) ""
No instance for (G.Vector U.Vector i) ""

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

谢谢你的帮助!

4

2 回答 2

4

我已经改变了4820b73的语法,所以你现在应该可以做你想做的事了:

derivingUnbox "Complex"
    [d| (Unbox a) ⇒ Complex a → (a, a) |]
    [| \ (r :+ i) → (r, i) |]
    [| \ (r, i) → r :+ i |]

我还修复了fe37976中的“xyz is not a (visible) method...”错误,尽管可以通过以下方式解决它:

import qualified Data.Vector.Generic
import qualified Data.Vector.Generic.Mutable

现在在Hackage上发布。抄送:@reinerp

于 2012-11-18T06:35:38.567 回答
3

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

TH方法

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

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

type family F a
instance Eq (F Int)
instance Eq (F Bool)

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

type instance F Int = Double
type instance F Bool = Double

然后两个实例Eq实际上是重叠的。

您遇到了vector-th-unbox包裹问题

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

instance (Unbox (IntType q), Foo q) => Unbox (Zq q) where
    ...

问题是该vector-th-unbox包迫使您使用假类型类Unbox'来传达中间表示类型(IntType q在您的情况下),作为滥用模板 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)

但不要这样做!

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

于 2012-07-10T22:10:44.007 回答