Dieser CodeHaskell GADT ‚Show'- Instance-Typ-Variable Abzug
{-# LANGUAGE GADTs #-}
data Expr a where
Val :: Num a => a -> Expr a
Eq :: Eq a => Expr a -> Expr a -> Expr Bool
eval :: Expr a -> a
eval (Val x) = x
eval (Eq x y) = (eval x) == (eval y)
instance Show a => Show (Expr a) where
show (Val x) = "Val " ++ (show x)
show (Eq x y) = "Eq (" ++ (show y) ++ ") (" ++ (show x) ++ ")"
mit der folgenden Fehlermeldung kompilieren fehlschlägt:
Test.hs:13:32: error:
* Could not deduce (Show a1) arising from a use of `show'
from the context: Show a
bound by the instance declaration at test.hs:11:10-32
or from: (a ~ Bool, Eq a1)
bound by a pattern with constructor:
Eq :: forall a. Eq a => Expr a -> Expr a -> Expr Bool,
in an equation for `show'
at test.hs:13:11-16
Possible fix:
add (Show a1) to the context of the data constructor `Eq'
* In the first argument of `(++)', namely `(show y)'
In the second argument of `(++)', namely
`(show y) ++ ") (" ++ (show x) ++ ")"'
In the expression: "Eq (" ++ (show y) ++ ") (" ++ (show x) ++ ")" Failed, modules loaded: none.
die letzte Zeile behebt das Problem, kommentiert und Inspektion der Der Typ von Expr
in GHCi zeigt, dass GHC, anstatt Eq
vom Typ Eq a => (Expr a) -> (Expr a) -> Expr Bool
zu folgern, es tatsächlich zu Eq a1 => (Expr a1) -> (Expr a1) -> Expr Bool
für data Expr a where ...
wird. Dies erklärt die Fehlermeldung, da instance Show a => Show (Expr a) where ...
a1
nicht als Instanz von Show
erzwingen wird.
aber ich nicht verstehen, warum GHC wählt, dies zu tun. Wenn ich eine Vermutung abgeben müsste, würde ich sagen, es hat etwas damit zu tun, dass Eq
einen Wert zurückgibt, der nicht von a
überhaupt abhängt und somit GHC "vergisst" über a
, sobald Eq
einen Expr Bool
zurückgibt. Ist das - zumindest was - hier passiert?
Gibt es auch eine saubere Lösung? Ich habe versucht, verschiedene Dinge, einschließlich versuchen, den gewünschten Typen zu „zwingen“ über InstanceSigs
und ScopedTypeVariables
so etwas wie dies zu tun:
instance Show a => Show (Expr a) where
show :: forall a. Eq a => Expr a -> String
show (Eq x y) = "Eq (" ++ (show (x :: Expr a)) ++ ") (" ++ (show (y :: Expr a)) ++ ")"
...
, aber ohne Erfolg. Und da ich immer noch ein Haskell-Neuling bin, bin ich mir nicht einmal sicher, ob das irgendwie irgendwie funktionieren könnte, aufgrund meiner ersten Vermutung, warum GHC überhaupt nicht den "richtigen"/gewünschten Typ ableitet.
EDIT:
Ok, so habe ich beschlossen, eine Funktion
extract (Eq x _) = x
in die Datei hinzuzufügen (ohne Typen Signatur), nur um zu sehen, welcher Rückgabetyp es haben würde. GHC weigerte sich erneut zu kompilieren, aber diesmal enthielt die Fehlermeldung einige Informationen über Variablen vom Skolem-Typ. Eine schnelle Suche ergab this question, was (glaube ich?) Formalisiert, was ich glaube das Problem zu sein: Sobald Eq :: Expr a -> Expr a -> Expr Bool
eine Expr Bool
zurückgibt, geht a
"out of scope". Jetzt haben wir keine Informationen mehr über a
, also extract
müsste die Signatur exists a. Expr Bool -> a
haben, da forall a. Expr Bool -> a
nicht für jede a
gilt. Da GHC exists
nicht unterstützt, schlägt die Typprüfung fehl.
Die einfachste Lösung besteht darin, die Signatur von Eq von 'Eq a => Expr a -> Expr a -> Expr Bool 'zu ändern, was erlaubt, dass' a 'in diesem Kontext der numerische Typ ist (was sein kann oder nicht) Instanz der Klasse show), to '(Eq a, Zeige a) => Expr a -> Expr a -> Expr Bool ', was erlaubt, eq only Ausdruck nur beim Vergleich von Ausdrücken von showable type zu konstruieren. – Antisthenes
Danke, das funktioniert; aber eine Lösung ohne diese zusätzliche Anforderung im Typ Konstruktor wäre auch interessant –
Mit dem ursprünglichen Typ gibt es keine gute Lösung.Es gibt einfach zu wenig, was man mit einem existentiellen Typ anfangen kann. Eine andere Möglichkeit besteht darin, "Val" auf einen festen Typ zu beschränken (oder mehrere "Val" -ähnliche Konstruktoren zu haben, die eine endliche Menge von Typen darstellen). –