Diversão com tipos! Resolvendo Declarações de Várias Instâncias
Eu estou tentando escrever algum código Haskell em que existem vários tipos de dados, cada um dos quais pode ter várias implementações. Para fazer isso, eu defino cada tipo de dados como umclass
cujos métodos são os construtores e seletores relevantes e, em seguida, implementam todas as operações em membros dessa classe em termos dos construtores e seletores fornecidos.
Por exemplo, talvezA
é uma classe polinomial (com métodosgetCoefficients
emakePolynomial
) que pode ter uma representação comoSparsePoly
ou umDensePoly
eB
é uma classe numérica complexa (com métodosgetReal
, getImag
emakeComplex
), que pode ser representado como umComplexCartesian
ou umComplexPolar
.
Eu reproduzi um exemplo mínimo abaixo. Eu tenho duas classesA
eB
cada um dos quais tem uma implementação. Eu quero fazer todas as instâncias de ambas as classes em instâncias deNum
automaticamente (isso requer aFlexibleInstances
eUndecidableInstances
tipo extensões). Isso funciona bem quando eu só tenho um dosA
ouB
, mas quando tento compilar com ambos, recebo o seguinte erro:
<code>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 </code>
Eu suponho que a mensagem 'declarações de instâncias duplicadas' é porque um tipo de dados pode ser feito uma instância de ambosA
eB
. Eu quero ser capaz de fazer uma promessa ao compilador que eu não farei isso, ou possivelmente especificar uma classe padrão para usar no caso em que um tipo é uma instância de ambas as classes.
Existe uma maneira de fazer isso (talvez outra extensão de tipo?) Ou isso é algo que eu estou preso?
Aqui está meu código:
<code>{-# 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 </code>
Editar: Para deixar claro, não estou tentando escrever nenhum código prático usando essa técnica. Eu estou fazendo isso como um exercício para me ajudar a entender melhor o sistema de tipos e as extensões.