Reputation: 6156
Imagine I want to define a vector space typeclass. I do the following (inspired by 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
That works perfectly. Now for some common instances. Of course, a tuple of fractionals is a vector space:
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)
Or even simpler, a fractional is a vector space over itself:
instance Fractional a => VectorSpace a where
type Groundfield a = a
(^+^) = (+)
zeroVector = 0
(^*) = (*)
Each of the instances itself is perfectly valid. But if I put them in the same module, I get this problem:
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
I realise that matching on a
in the second instance definition catches a tuple pattern as well. But I would have expected that I've already matched that pattern if I write the instance definition for (a,a)
before. Apparently not! How can I achieve this?
Upvotes: 2
Views: 165
Reputation: 38901
With the help of GHC's (relatively) new closed type families, I think we can pull it off. The following all typechecks:
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)
Upvotes: 1