danbroooks
danbroooks

Reputation: 2800

Using type constraints across typeclass instances

I'm trying to define a type class Plotable that provides a function plotable to return a tuple representing co-ordinates in a chart (x,y), it the type of x and y does not have to be specific (ie Double), I think they can be any number type (they're being handed over to Chart).

I want plotable to be able to deal with Num a => Complex a and Num a => (a, a), so I wrote:

class Plotable a where
  plotable :: Num b => a -> (b, b)

instance Num a => Plotable (a, a) where
  plotable = id

instance Num a => Plotable (Complex a) where
  plotable c = (realPart c, imagPart c)

Which makes sense to me, however I'm getting the error:

Couldn't match type ‘a’ with ‘b’
  ‘a’ is a rigid type variable bound by
    the instance declaration
    at /Users/dan.brooks/Code/haskell/coding-the-matrix/src/TheField/Plot.hs:12:10-33
  ‘b’ is a rigid type variable bound by
    the type signature for:
      plotable :: forall b. Num b => (a, a) -> (b, b)
    at /Users/dan.brooks/Code/haskell/coding-the-matrix/src/TheField/Plot.hs:13:3-10
  Expected type: (a, a) -> (b, b)
    Actual type: (b, b) -> (b, b)

But surely if a is constrained to Num, and b is constrained to Num, passing through the same value should work in this case? Is this simply a limitation of the compilier? or am I misusing typeclasses and type constraints?

Upvotes: 0

Views: 72

Answers (1)

chi
chi

Reputation: 116139

plotable :: Num b => a -> (b, b)

means that any of these must type check

plotable :: a -> (Int, Int)
plotable :: a -> (Integer, Integer)
plotable :: a -> (Double, Double)
...

In other words, that polymorphic type promises to the user that plotable can use whichever numeric type b the user can choose.

id :: a -> a does not let the user choose b, so it is not general enough.

Upvotes: 1

Related Questions