Question

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?

Était-ce utile?

La solution

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)

Autres conseils

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).

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top