Haskell 如何使用类型族定义这些重叠实例?

Haskell 如何使用类型族定义这些重叠实例?,haskell,typeclass,type-families,Haskell,Typeclass,Type Families,假设我想定义一个向量空间类型类。我做了以下工作(受Yampa启发): 那很好用。现在来看一些常见的例子。当然,分数元组是一个向量空间: instance Fractional a => VectorSpace (a,a) where type Groundfield (a,a) = a (a, b) ^+^ (c, d) = (a + c, b + d) zeroVector = (0,0) (a, b) ^* c

假设我想定义一个向量空间类型类。我做了以下工作(受Yampa启发):

那很好用。现在来看一些常见的例子。当然,分数元组是一个向量空间:

instance Fractional a => VectorSpace (a,a) where
    type Groundfield (a,a) = a
    (a, b) ^+^ (c, d)      = (a + c, b + d)
    zeroVector             = (0,0)
    (a, b) ^* c            = (a * c, b * c)
更简单的是,分数是一个向量空间:

instance Fractional a => VectorSpace a where
    type Groundfield a = a
    (^+^)              = (+)
    zeroVector         = 0
    (^*)               = (*)
每个实例本身都是完全有效的。但是如果我把它们放在同一个模块中,我会遇到这个问题:

VectorSpielwiese.hs:15:10:
  Conflicting family instance declarations:
    Groundfield (a, a) -- Defined at VectorSpielwiese.hs:15:10
    Groundfield a -- Defined at VectorSpielwiese.hs:21:10
我意识到,在第二个实例定义中,匹配
a
也会捕获元组模式。但是如果我以前为
(a,a)
编写实例定义,我会认为我已经匹配了该模式。显然不是!我如何才能做到这一点?

在GHC(相对而言)新型封闭式系列的帮助下,我认为我们可以实现这一目标。以下是所有类型检查:

type family Groundfield v where
    Groundfield (a,a) = a
    Groundfield a = a

class Fractional (Groundfield v) => VectorSpace v where
    (^+^)      :: v -> v -> v
    zeroVector :: v
    (^*)       :: v -> Groundfield v -> v

instance (Fractional (Groundfield a), Num a, a ~ Groundfield a) => VectorSpace a where
    (^+^)              = (+)
    zeroVector         = 0
    (^*)               = (*)

instance Fractional a => VectorSpace (a,a) where
    (a, b) ^+^ (c, d)      = (a + c, b + d)
    zeroVector             = (0,0)
    (a, b) ^* c            = (a * c, b * c)

除非将第二个实例更改为具有新类型包装器,或者将
GroundField
更改为数据族,否则不能这样做。我怀疑这两个都不是你想要的。在这种情况下通常采用的方法是有一个“默认”实现,它对应于这个“普通”向量空间,然后您只需编写
实例向量空间Float;实例向量空间双精度。为此,您可能需要
DefaultSignatures
。这就是为什么:它为
Float
Double
CIntMax
并且只使复合实例具有多态性。@leftaroundabout,是的,那是我开始分叉的时候,我想“这是很多重复。让我们改进一下。”@Turion:是的,这确实是非常机械的重复,你可以用一个简单的CPP宏很好地实现它。我同意这不太好。。。基本上,我们想要的是一个单独的实例,它适用于一组封闭的类型,也许您可以使用它更好地实现实例。哇!但是,如果这两个实例位于不同的模块中,该怎么办?闭合类型族强制同时声明Groundfield的所有实例。但是向量空间的实例可以在任何地方声明。
type family Groundfield v where
    Groundfield (a,a) = a
    Groundfield a = a

class Fractional (Groundfield v) => VectorSpace v where
    (^+^)      :: v -> v -> v
    zeroVector :: v
    (^*)       :: v -> Groundfield v -> v

instance (Fractional (Groundfield a), Num a, a ~ Groundfield a) => VectorSpace a where
    (^+^)              = (+)
    zeroVector         = 0
    (^*)               = (*)

instance Fractional a => VectorSpace (a,a) where
    (a, b) ^+^ (c, d)      = (a + c, b + d)
    zeroVector             = (0,0)
    (a, b) ^* c            = (a * c, b * c)