Betrachten Sie diesen Code:
%Vor% Hier ruft foo
bar
auf und sollte dies mit der Einschränkung SomeClass
in seinem Kontext tun können. Stattdessen nimmt GHC an, dass dies mit der Foo a => SomeClass a
-Instanz tun muss:
Es gibt zwei Möglichkeiten, dies zu beheben:
foo
, Änderung von let x = bar t in x
in bar t
instance SomeClass Int
an mein Programm Was ist hier los? Warum tritt dieses Problem auf und warum funktionieren diese Fixes?
Dieses Beispiel wird natürlich durch mein tatsächliches Problem vereinfacht. Ich habe es während meiner Arbeit am Cubix-Framework für mehrsprachige Transformation (arxiv.org/pdf/1707.04600) angetroffen.
Mein tatsächliches Problem beinhaltet eine Funktion mit einer Einschränkung InjF f IdentL FunctionExpL
("f ist eine Sprache, in der Bezeichner verwendet werden können, um die Funktion in einem Funktionsaufruf zu bezeichnen"). Ich habe eine (überlagerbare) Instanz (FunctionIdent :<: f) => InjF f IdentL FunctionExpL
, auf die der Typchecker greift, was mir einen falschen Could not deduce FunctionIdent :<: f
-Fehler gibt.
Dieser Fehler bleibt auch bestehen, wenn es eine Instanz InjF Foo IdentL FunctionExpL
im Bereich für eine bestimmte Foo
gibt. Die Antwort von lovelaroundabout, die voraussagt, dass diese andere Instanz das Problem beheben sollte, ist nicht die ganze Geschichte.
Der Grund dafür ist, dass der Compiler versucht, x
so allgemein wie möglich zu machen: Er möchte, dass es (SomeClass a) => Int
ist (beachte, dass dies ein mehrdeutiger Typ wäre, wenn du es selbst geschrieben hättest). Eine Möglichkeit, diesen seltsamen lokalen Typ zu verhindern, ist -XMonoLocalBinds
zu aktivieren, aber ich würde es nicht wirklich empfehlen.
Nun fährt der Compiler mit der Typisierung fort. An diesem Punkt gibt es genau einen instance SomeClass
im Bereich, nämlich den Catch-all (Foo a) => SomeClass a
, also gibt es keine Mehrdeutigkeit. (Im Prinzip verbietet Haskell die Mehrdeutigkeit bei der Auflösung von Instanzen; OVERLAPPABLE
untergräbt dies, aber es springt nur dann in Aktion, wenn es wirklich notwendig ist, wie zB wenn Sie ein zusätzliches instance SomeClass Int
haben.) Der Compiler wird daher sofort an diese Instanz gebunden Damit entfällt (SomeClass a)
für die Typprüfung von c
. Dies ist in Situationen erforderlich, in denen eine Klassenbedingung durch die Einschränkung einer Instanz ersetzt werden soll. Dies mag in dem gegebenen Beispiel nutzlos erscheinen, aber es ist tatsächlich von entscheidender Bedeutung, wenn Sie Instanzen des Formulars haben
... das ist viel vernünftiger als Ihr Code, weil nicht als Oberklassenbeschränkung ausgedrückt werden kann, und vollkommen in Ordnung ist, ohne Überlappungen. Wenn der Compiler die Constraint-Auflösung in diesen Situationen nicht auf Bar a
reduziert hat, konnte er den Maybe
-Typ niemals wirklich auflösen.
Schlussfolgerung: Vermeiden Sie überlappende Instanzen; Wenn möglich, Klassenrelationen durch Deklarationen der Superklassen ausdrücken, ansonsten die Constraints in eine GADT einordnen (was zwar umständlich ist, aber genaue Kontrolle darüber gibt, wo welche Constraint verwendet wird).