I am playing around with some type-level programming, and I wanted to implement some interesting instances for things like Num
, allowing mathematical operators on fixed-length Vector
s.
Some of the pertinent parts I currently have are as follows (in addition to a Functor
instance):
data Nat = One
| Succ Nat
deriving (Show, Eq)
data Vector (n :: Nat) a where
VecSing :: a -> Vector 'One a
(:+) :: a -> Vector n a -> Vector ('Succ n) a
instance Num a => Num (Vector 'One a) where
(+) = vecZipWith (+)
(*) = vecZipWith (*)
abs = fmap abs
signum = fmap signum
negate = fmap negate
fromInteger = VecSing . fromInteger
instance (Num a, Num (Vector n a)) => Num (Vector ('Succ n) a) where
(+) = vecZipWith (+)
(*) = vecZipWith (*)
abs = fmap abs
signum = fmap signum
negate = fmap negate
fromInteger a = fromInteger a :+ fromInteger a
However, I have come up with an issue when I actually want to use these instances. This is due to the fact, as much as I can surmise, that whenever I want to add two Vector
s together, I have to specify that they are, in fact, instances of Num
, such as in the below example.
addAndMultiply :: (Num a) => Vector n a -> Vector n a -> Vector n a -> Vector n a
addAndMultiply a b c = (a + b) * c
-- Compilation error snippet:
-- Could not deduce (Num (Vector n a)) arising from a use of `*'
-- from the context: Num a
What I would like is a way to imply that, when you have a Num a
, any Vector n a
s also fulfill the constraint Num
.
I tried to look at this solution, and this one, but I don't think these are quite the same thing; the question-askers usually have a data type that takes a constraint, and does things with the boxed value, while I just want to implicitly imply a constraint from another one; or at least, I think I do.
Is this possible? If so, how?