I have a class:
import Linear
class Coordinate c where
rotate :: Num a => Quaternion a -> c a -> c a
translate :: Num a => V3 a -> c a -> c a
, for which I have defined the instances
instance Coordinate V3 where
rotate _ = id
translate p = (p+)
instance Coordinate Quaternion where
rotate o = (o*)
translate _ = id
Now I want to define an instance for a pair of members of the class.
instance (Coordinate a, Coordinate b) => Coordinate (a, b) where
rotate o (a, b) = (rotate o a, rotate o b)
translate p (a, b) = (translate p a, translate p b)
The problem is that this does not work, since the compiler expects an argument for a
and b
. However adding a type-constraint like
instance (Coordinate a, Coordinate b, Num c) => Coordinate (a c, b c) where
move p (a, b) = (move p a, move p b)
translate p (a, b) = (translate p a, translate p b)
It does not work either, since this results in an expression with the kind *
rather than * -> *
. I can see how both of the above are incorrect, but I am unsure of how to solve this. I suppose there should be some form of constraint that keeps the Num
types for both a
and b
the same, but I don't know what that would look like syntactically.
You cannot make an instance of this Coordinate
class for the built-in pair type. You need to change one of them.
The Coordinate
class can be changed to take a normal Type
as argument:
{-# LANGUAGE FlexibleContexts, TypeFamilies #-}
import Data.Kind (Type)
class Num (Component c) => Coordinate c where
type Component c :: Type -- every Coordinate type has a Component type
rotate :: Quaternion (Component c) -> c -> c
translate :: V3 (Component c) -> c -> c
E.g the V3
instance will now look like
instance Num a => Coordinate (V3 a) where
type Component (V3 a) = a
rotate _ = id
translate = (+)
And the pair instance will use an equality constraint, which is the thing you were looking for
instance (Coordinate a, Coordinate b, Component a ~ Component b) => Coordinate (a, b) where
type Component (a, b) = Component a -- or = Component b
rotate p (l, r) = (rotate p l, rotate p r)
translate p (l, r) = (translate p l, translate p r)
Instead of pairs, use Product
:
import Data.Functor.Product
instance (Coordinate a, Coordinate b) => Coordinate (Product a b) where
rotate p (Pair l r) = Pair (rotate p l) (rotate p r)
translate p (Pair l r) = Pair (translate p l) (translate p r)