Я пытаюсь написать код на Haskell, в котором есть несколько типов данных, каждый из которых может иметь несколько реализаций. Для этого я определяю каждый тип данных как класс
, методы которого являются соответствующими конструкторами и селекторами, а затем реализую все операции над членами этого класса в терминах заданных конструкторов и селекторов.
Например, возможно, A
является полиномиальным классом (с методами getCoefficients
и makePolynomial
), который может иметь представление как SparsePoly
. ] или DensePoly
и B
— класс комплексных чисел (с методами getReal
, getImag
и makeComplex
) который может быть представлен как ComplexCartesian
или ComplexPolar
.
Ниже я воспроизвел минимальный пример. У меня есть два класса A
и B
, каждый из которых имеет реализацию. Я хочу автоматически превратить все экземпляры обоих классов в экземпляры Num
(для этого требуются расширения типа FlexibleInstances
и UndecidableInstances
).Это прекрасно работает, когда у меня есть только один из A
или B
, но когда я пытаюсь скомпилировать оба, я получаю следующую ошибку:
Duplicate instance declarations:
instance [overlap ok] (A a, Num x, Show (a x), Eq (a x)) =>
Num (a x)
-- Defined at test.hs:13:10-56
instance [overlap ok] (B b, Num x, Show (b x), Eq (b x)) =>
Num (b x)
-- Defined at test.hs:27:10-56
Я полагаю, что «дубликат экземпляра объявления, потому что тип данных может быть сделан экземпляром обоих A
и B
. Я хочу иметь возможность дать обещание компилятору, что я не буду этого делать, или, возможно, указать класс по умолчанию для использования в случае, если тип является экземпляром обоих классов.
Есть ли способ сделать это (может быть, другое расширение типа?) или это то, с чем я застрял?
Вот мой код:
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}
class A a where
fa :: a x -> x
ga :: x -> a x
data AImpl x = AImpl x deriving (Eq,Show)
instance A AImpl where
fa (AImpl x) = x
ga x = AImpl x
instance (A a, Num x, Show (a x), Eq (a x)) => Num (a x) where
a1 + a2 = ga (fa a1 + fa a2)
-- other implementations go here
class B b where
fb :: b x -> x
gb :: x -> b x
data BImpl x = BImpl x deriving (Eq,Show)
instance B BImpl where
fb (BImpl x) = x
gb x = BImpl x
instance (B b, Num x, Show (b x), Eq (b x)) => Num (b x) where
-- implementations go here
Редактировать: Чтобы было ясно, я не пытаюсь писать практический код, используя эту технику. Я делаю это как упражнение, чтобы помочь себе лучше понять систему типов и расширения.