Reputation: 33
I've been running through the "Learn You a Haskell" book, and I'm trying to wrap my head around Haskell Type Classes. As practice, I'm trying to create a simple vector type class. The following snippet of code has been giving me some grief (resulting in my first post to StackOverflow):
data Vec2 a = Vec2 (a,a) deriving (Show, Eq, Read)
class Vector a where
(<*) :: (Num b) => a -> b -> a
instance (Num a) => Vector (Vec2 a) where
Vec2 (x,y) <* a = Vec2 (a*x, a*y)
I get the following error message:
Could not deduce (a~b) from the context (Num a) or from (Num b) bound by the type signature for
<* :: Num b => Vec2 a -> b -> Vec2 a
It seems like the Num
being specified in the typeclass should supply the type of a
, and Num a
spefication in the instance should supply the type of x
and y
, so why is it complaining? What misconception do I have about this code?
Upvotes: 3
Views: 1359
Reputation: 3434
I think the problem is (*)
has the type (Num a) => a -> a -> a
rather than (Num a, Num b) => a -> b -> a
that complier expected.
I'm not familiar with Haskell's number conversion but I have a limited workaround, given the second argument is an instance of Integral
.
data Vec2 a = Vec2 (a,a) deriving (Show, Eq, Read)
class Vector a where
(<*) :: (Integral b) => a -> b -> a
instance (Num a) => Vector (Vec2 a) where
Vec2 (x,y) <* a = Vec2 (x*b, y*b)
where b = fromIntegral a
because fromIntegral
has type (Integral a, Num b) => a -> b
, it can convert the second argument of *
as needed.
Upvotes: 0
Reputation: 11208
The type of (*) :: Num a => a -> a -> a
. But when you are actually trying to use *
, you are actually multiplying two unrelated types having Num
instances and the compiler is not able to infer that they are same.
To explain it more clearly look at the type of <*
and the universal quantification of b
(<*) :: (Num b) => a -> b -> a
What you are saying here is, give me any type having Num
instance and I will be able to multiply that with my vector, but what you want to say is something different.
You need to some how say that a
in the type Vec2 a
is same as b
in the type (<*) :: Num b => a -> b -> a
, only then you can multiply them together. Here is a solution using typefamilies to ensure this constraint.
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
data Vec2 a = Vec2 (a,a) deriving (Show, Eq, Read)
class (Num (VectorOf a)) => Vector a where
type VectorOf a :: *
(<*) :: a -> (VectorOf a) -> a
instance (Num a) => Vector (Vec2 a) where
type VectorOf (Vec2 a) = a
Vec2 (x,y) <* a = Vec2 (a*x, a*y)
Upvotes: 7
Reputation: 3273
The compiler can't verify that both of the Num
instances involved are actually the same type. They're each Num
instances, sure, but what is also required is that they must be the same instance.
Otherwise, you could write something like this:
Vec2 (1 :: Double, 2 :: Double) <* (3 :: Int)
which doesn't fly when it comes time to do, ex: (1 :: Double) * (3 :: Int)
.
Upvotes: 0