如何使用类型系列定义这些重叠实例?

时间:2016-02-09 11:11:37

标签: haskell typeclass type-families

想象一下,我想定义一个向量空间类型类。我做了以下(灵感来自Yampa):

{-# LANGUAGE TypeFamilies           #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE UndecidableInstances   #-}

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

完美无缺。现在针对一些常见情况。当然,分数元组是一个向量空间:

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)的实例定义,我本可以预期我已经匹配了该模式。显然不是!我怎样才能做到这一点?

1 个答案:

答案 0 :(得分:1)

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