I have a class "Shape" which should have "area" defined on all instances. area returns "Area b" (a data type) that contains a number (b belongs to Num typeclass) signifying the area of that Shape.
Haskell has problem binding that b to (x*y) where x and y are of type 'a' and 'a' is also of typeclass Num.
How do I solve this ?? [If i replace (x*y) by 0, it works but doesn't work even with (0::Int)]
Code :
data Unit = Unit | Meter | CentiMeter deriving Show
data Area a = Area a Unit deriving Show
class Shape a where
area :: (Num b) => a -> Area b
data Rectangle side = Rectangle side side Unit deriving Show
instance (Num a) => Shape (Rectangle a) where
area (Rectangle x y unit) = Area (x*y) unit
Error :
[1 of 1] Compiling Main ( y.hs, interpreted )
y.hs:11:46:
Could not deduce (a ~ b)
from the context (Num a)
bound by the instance declaration at y.hs:10:10-39
or from (Num b)
bound by the type signature for
area :: Num b => Rectangle a -> Area b
at y.hs:11:10-52
`a' is a rigid type variable bound by
the instance declaration at y.hs:10:15
`b' is a rigid type variable bound by
the type signature for area :: Num b => Rectangle a -> Area b
at y.hs:11:10
In the second argument of `(*)', namely `y'
In the first argument of `Area', namely `(x * y)'
In the expression: Area (x * y) unit
Failed, modules loaded: none.
The problem here is area
's type signature:
area :: (Num b) => a -> Area b
What it says is "give me an a
, and I'll give you an Area b
for any b
you want; you can pick". So, for instance, I could give area
an Integer
and expect back an Area Double
. Clearly, this isn't what you want!
In this case, the error arises because you use x*y
, which has type a, when b is expected — you have to give a value that works for any numeric type b, but you're giving a value that only works for one (a).
If you changed area
's type to a -> Area Integer
, or such, then it would work. However, I have a feeling you want the instance to be able to specify what the type of the area is. For this, you'll need to use a language extension called type families:
{-# LANGUAGE TypeFamilies #-}
class (Num (AreaComponent a)) => Shape a where
type AreaComponent a
area :: a -> Area (AreaComponent a)
instance (Num a) => Shape (Rectangle a) where
type AreaComponent (Rectangle a) = a
area (Rectangle x y unit) = Area (x*y) unit
This says that for every type a that's an instance of Shape
, there is an associated type AreaComponent a
, representing the type of each component of its area. That type is required to be an instance of Num
by the definition of Shape
.
Another thing you could do, if all your shapes take a numeric type parameter, is to make the instances be for the type constructors of each shape, rather than the full shape types themselves:
class Shape sh where
area :: (Num a) => sh a -> Area a
instance Shape Rectangle where
area (Rectangle x y unit) = Area (x*y) unit
The problem is that
class Shape a where
area :: (Num b) => a -> Area b
promises to be able to deliver any Num
type that the caller might want, but your implementation just provides some Num
type the callee provides.
The only way to produce numbers of any desired type is to use fromInteger
(or literals, wher fromInteger
is implicit).
To be able to deliver some Num
type determined by the type of thing whose area is to be computed, you can use multi parameter type classes and functional dependencies or type families, e.g.
{-# LANGUAGE TypeFamilies #-}
class Shape a where
type NumType a :: *
area :: a -> Area (NumType a)
instance (Num side) => Shape (Rectangle side) where
type NumType (Rectangle side) = side
area (Rectangle w h u) = Area (w*h) u