Я экспериментирую с программированием на уровне типов, и я хотел реализовать несколько интересных примеров для таких вещей, как Num
, позволяющих математические операторы с фиксированной длиной Vector
s.
Некоторые из соответствующие части, которые у меня в настоящее время есть, следующие (в дополнение к экземпляру Functor
):
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
Однако я столкнулся с проблемой, когда я действительно хочу использовать эти экземпляры. Насколько я могу предположить, это связано с тем фактом, что всякий раз, когда я хочу сложить два Vector
вместе, я должен указать, что они на самом деле являются экземплярами Num
, например, в приведенном ниже примере .
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
То, что я хотел бы, - это способ обозначить, что, когда у вас есть Num a
, любые Vector n a
также удовлетворяют ограничению Num
.
Я пробовал посмотреть на это решение и это одно, но я не думаю, что это одно и то же; у задающих вопросы обычно есть тип данных, который принимает ограничение и делает что-то с упакованным значением, в то время как я просто хочу неявно подразумевать ограничение из другого; или, по крайней мере, я думаю, что да.
Возможно ли это? Если да, то как?