Klassen sind ein bisschen komisch. Die ursprüngliche Idee (die immer noch ziemlich funktioniert) ist eine Art von syntaktischem Zucker um was sonst wäre data
Aussagen. Zum Beispiel können Sie sich vorstellen:
data Num a = Num {plus :: a -> a -> a, ... , fromInt :: Integer -> a}
numInteger :: Num Integer
numInteger = Num (+) ... id
dann können Sie Funktionen schreiben, die z. Typ:
Also die Idee ist "wir werden automatisch alle diese Bibliothek Code ableiten." Die Frage ist, wie finden wir die Bibliothek, die wir wollen? Es ist einfach, wenn wir eine Bibliothek vom Typ Num Int
haben, aber wie tun erweitern wir es auf „eingeschränkte Instanzen“, basierend auf Funktionen vom Typ:
fooLib :: Foo x -> Bar x
xyyLib :: Xyy x -> Bar x
Die vorliegende Lösung in Haskell ist ein tun Art-Muster- passe die Ausgabetypen dieser Funktionen an und übertrage die Eingaben auf die resultierende Deklaration. Aber wenn es gibt zwei Ausgänge des gleichen Typs, würden wir eine combinator brauchen die diese in geht:
eitherLib :: Either (Foo x) (Xyy x) -> Bar x
und im Grunde das Problem ist, dass es kein guter Constraint-combinator dieser jetzt Art. Das ist dein Einwand.
Nun, das stimmt, aber es gibt Wege, etwas in der Praxis moralisch ähnlich zu erreichen. Angenommen, definieren wir einige Funktionen mit Typen:
data F
data X
foobar'lib :: Foo x -> Bar' x F
xyybar'lib :: Xyy x -> Bar' x X
bar'barlib :: Bar' x y -> Bar x
Klar, dass die y
eine Art „Phantom-Typ“ ist mit Gewinde durch all dies, aber es bleibt mächtig, weil da wir ein Bar x
wollen wir die Notwendigkeit für eine propagieren Bar' x y
und gegeben die Notwendigkeit für die Bar' x y
werden wir entweder Bar' x X
oder Bar' x y
generieren. Mit Phantom-Typen und Multi-Parameter-Typ-Klassen erhalten wir das gewünschte Ergebnis.
Weitere Informationen: https://www.haskell.org/haskellwiki/GHC/AdvancedOverlap
Welche Instanz sollte GHC wählen, ob 'a' ein 'foo' ist und ein' Xyy'? – mb14
@ mb14: Eine beliebige. ('InkoherentInstances' macht schon etwas ähnliches, und damit kann ich leben). – ron
Tatsächlich scheint der einzige Unterschied darin zu bestehen, dass 'InkohärenteInstanzen' es GHC erlauben, sich an eine der beiden Instanzen zu binden, möglicherweise einen mit einem erfüllbaren Kontext zu verwerfen und sich an den unerfüllbaren zu binden (was einen Fehler auslösen wird). Diese Frage fragt stattdessen, warum GHC keine "BacktrackOnContextFailures" -Flagge hat, wenn ich richtig verstehe, so dass die richtige Instanz schließlich versucht wird. Es wird im schlimmsten Fall sicherlich zur Unnachgiebigkeit führen, aber wir haben bereits "UnentscheidbareInstanzen", die die Leistung erheblich beeinträchtigen können. – chi