newtype Vector2 a = Vector2 (a,a)
deriving (Show,Eq)
class VectorSpace v where
vZero :: (Num a) => v a
vSum :: (Num a) => v a -> v a -> v a
So here is what I tried so far:
instance VectorSpace (a,a) => VectorSpace Vector2 a vecZero = (0.0,0.0) vecSum (x,y) (x',y') = (x+x',y+y')
The first problem here is syntax. You need a where
at the end of the first line, and if Vector2 a
is supposed to be the instance head then it needs to go in parentheses:
instance VectorSpace (a,a) => VectorSpace (Vector2 a) where
That, however, doesn't match the kinds of your declared class.
class VectorSpace (v :: * -> *) where
vZero :: (Num a) => v a
...
i.e., the class already has the assumption built in that v
will be applied to some a
parameter†. Thus the instance head should not contain that parameter, it should just look like
instance (...?) => VectorSpace Vector2 where
In fact it turns out you don't need any constraints at all here.
instance VectorSpace Vector2 where
Now as for the methods,
vecSum (x,y) (x',y') = (x+x',y+y')
that would be a perfectly sensible implementation if your type were the tuple type. However your type is actually a newtype
wrapped tuple, and newtypes
always need explicit constructors. Like
vecSum (Vector2 (x,y)) (Vector2 (x',y')) = Vector2 (x+x',y+y')
This is a bit silly really: you have both a named constructor and a tuple constructor, nested. It's also pretty inefficient since tuples incur extra indirection (laziness, cache). The type should better be defined as
data Vector2 a = Vector2 !a !a
where, because the fields are strict, GHC can unbox the numbers. In that case, the definition would be
vecSum (Vector2 x y) (Vector2 x' y') = Vector2 (x+x') (y+y')
†Mind, as I've already commented it is IMO not good for a vector space class to parameterise v a
at all. In the vector-space library, the instances aren't required to be parameterised; one of the advantages is that you can directly give an instance for ordinary tuples without needing any newtype wrapping.