Spaß mit Typen! Mehrere Instanzdeklarationen auflösen
Ich versuche, einen Haskell-Code zu schreiben, in dem es mehrere Datentypen gibt, von denen jeder mehrere Implementierungen haben kann. Dazu definiere ich jeden Datentyp alsclass
deren Methoden die relevanten Konstruktoren und Selektoren sind, und dann alle Operationen auf Mitgliedern dieser Klasse in Bezug auf die angegebenen Konstruktoren und Selektoren implementieren.
Zum Beispiel vielleichtA
ist eine Polynomklasse (mit MethodengetCoefficients
undmakePolynomial
), die eine Darstellung alsSparsePoly
oder einDensePoly
undB
ist eine komplexe Zahlenklasse (mit MethodengetReal
, getImag
undmakeComplex
), die als dargestellt werden kannComplexCartesian
oder einComplexPolar
.
Ich habe ein minimales Beispiel unten reproduziert. Ich habe zwei KlassenA
undB
Jedes davon hat eine Implementierung. Ich möchte alle Instanzen beider Klassen zu Instanzen von machenNum
automatisch (dies erfordert dieFlexibleInstances
undUndecidableInstances
Typerweiterungen). Dies funktioniert gut, wenn ich nur eine vonA
oderB
Wenn ich jedoch versuche, mit beiden zu kompilieren, wird die folgende Fehlermeldung angezeigt:
<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>
Ich nehme an, dass die Meldung "Instanzdeklarationen duplizieren" vorliegt, weil ein Datentyp zu einer Instanz von beiden gemacht werden könnteA
undB
. Ich möchte dem Compiler versprechen, dass ich das nicht tue, oder möglicherweise eine Standardklasse angeben, die verwendet werden soll, wenn ein Typ eine Instanz beider Klassen ist.
Gibt es eine Möglichkeit, dies zu tun (vielleicht eine andere Typerweiterung?) Oder ist dies etwas, mit dem ich festgefahren bin?
Hier ist mein Code:
<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>
Bearbeiten: Um mich klar zu machen, versuche ich nicht, mit dieser Technik praktischen Code zu schreiben. Ich mache es als Übung, um das Typensystem und die Erweiterungen besser zu verstehen.