Estoy intentando escribir algún código Haskell en el que haya múltiples tipos de datos, cada uno de los cuales puede tener múltiples implementaciones. Para hacer esto, defino cada tipo de datos como class
cuyos métodos son los constructores y selectores relevantes, y luego implemento todas las operaciones en los miembros de esa clase en términos de los constructores y selectores dados.¡Diversión con tipos! Resolución de declaraciones de instancias múltiples
Por ejemplo, tal vez A
es una clase polinomio (con métodos getCoefficients
y makePolynomial
) que pueden tener una representación como SparsePoly
o una DensePoly
y B
es una clase número complejo (con métodos getReal
, getImag
y makeComplex
) que puede ser representado como ComplexCartesian
o ComplexPolar
.
He reproducido un ejemplo mínimo a continuación. Tengo dos clases A
y B
, cada una de las cuales tiene una implementación. Quiero hacer todas las instancias de ambas clases en instancias de Num
automáticamente (esto requiere las extensiones de tipo FlexibleInstances
y UndecidableInstances
). Esto funciona bien cuando sólo tengo uno de A
o B
, pero cuando intento compilar con ambos, me sale el siguiente error:
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
supongo que el mensaje de los 'declaraciones de instancias duplicadas' se debe a un tipo de datos podría hacerse una instancia de ambos A
y B
. Quiero ser capaz de hacer una promesa al compilador de que no haré eso, o posiblemente especifique una clase predeterminada para usar en el caso de que un tipo sea una instancia de ambas clases.
¿Hay alguna manera de hacer esto (quizás otra extensión de tipo?) ¿O es algo con lo que estoy atascado?
Aquí está mi código:
{-# 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
Editar: Para quedado claro, no estoy tratando de escribir cualquier código de práctica utilizando esta técnica. Lo hago como ejercicio para ayudarme a comprender mejor el sistema de tipos y las extensiones.
Relacionado: [Cómo escribo, "si typeclass a, then a también es una instancia de b según esta definición."] (Http://stackoverflow.com/a/3216937/98117). – hammar