Turion
Turion

Reputation: 6156

How to define these overlapping instances with type families?

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

Answers (1)

sclv
sclv

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

Related Questions