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 oderBWenn 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.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage