Beim Kompilieren meiner Haskell-Anwendung mit der -Wall
Option beschwert sich GHC über verwaiste Instanzen, zum Beispiel:
Publisher.hs:45:9:
Warning: orphan instance: instance ToSElem Result
Die Typklasse ToSElem
gehört nicht mir, sondern wird von HStringTemplate definiert .
Jetzt weiß ich, wie ich das beheben kann (verschieben Sie die Instanzdeklaration in das Modul, in dem das Ergebnis deklariert ist), und ich weiß, warum GHC verwaiste Instanzen lieber vermeiden möchte , aber ich glaube immer noch, dass mein Weg besser ist. Es ist mir egal, ob der Compiler unangenehm ist - eher als ich.
Der Grund, warum ich meine ToSElem
Instanzen im Publisher-Modul deklarieren möchte, ist, dass das Publisher-Modul von HStringTemplate abhängt und nicht von den anderen Modulen. Ich versuche, eine Trennung der Bedenken aufrechtzuerhalten und zu vermeiden, dass jedes Modul von HStringTemplate abhängt.
Ich dachte, dass einer der Vorteile von Haskells Typklassen im Vergleich zu Javas Schnittstellen darin besteht, dass sie offen und nicht geschlossen sind und daher die Instanzen nicht an derselben Stelle wie der Datentyp deklariert werden müssen. Der Rat von GHC scheint zu sein, dies zu ignorieren.
Was ich also suche, ist entweder eine Bestätigung, dass mein Denken vernünftig ist und dass ich berechtigt wäre, diese Warnung zu ignorieren / zu unterdrücken, oder ein überzeugenderes Argument dagegen, Dinge auf meine Weise zu tun.
Antworten:
Ich verstehe, warum Sie dies tun möchten, aber leider kann es nur eine Illusion sein, dass Haskell-Klassen so "offen" zu sein scheinen, wie Sie es sagen. Viele Leute glauben, dass die Möglichkeit, dies zu tun, ein Fehler in der Haskell-Spezifikation ist, aus Gründen, die ich unten erläutern werde. Wenn es für die Instanz wirklich nicht geeignet ist, müssen Sie entweder in dem Modul deklariert werden, in dem die Klasse deklariert ist, oder in dem Modul, in dem der Typ deklariert ist. Dies ist wahrscheinlich ein Zeichen dafür, dass Sie einen
newtype
oder einen anderen Wrapper verwenden sollten um Ihren Typ.Die Gründe, warum verwaiste Instanzen vermieden werden müssen, liegen weit über dem Komfort des Compilers. Dieses Thema ist ziemlich kontrovers, wie Sie anderen Antworten entnehmen können. Um die Diskussion auszugleichen, werde ich den Standpunkt erläutern, dass man niemals verwaiste Instanzen schreiben sollte, was meiner Meinung nach die Mehrheitsmeinung unter erfahrenen Haskellern ist. Meine eigene Meinung ist irgendwo in der Mitte, was ich am Ende erklären werde.
Das Problem ergibt sich aus der Tatsache, dass es in Standard-Haskell keinen Mechanismus gibt, um anzugeben, welche verwendet werden soll, wenn mehr als eine Instanzdeklaration für dieselbe Klasse und denselben Typ vorhanden ist. Vielmehr wird das Programm vom Compiler abgelehnt.
Der einfachste Effekt davon ist, dass Sie ein perfekt funktionierendes Programm haben könnten, das plötzlich aufgrund einer Änderung, die jemand anderes in einer weit entfernten Abhängigkeit von Ihrem Modul vornimmt, nicht mehr kompiliert wird.
Schlimmer noch, es ist möglich, dass ein Arbeitsprogramm zur Laufzeit aufgrund einer entfernten Änderung abstürzt . Sie könnten eine Methode verwenden, von der Sie annehmen, dass sie aus einer bestimmten Instanzdeklaration stammt, und sie könnte stillschweigend durch eine andere Instanz ersetzt werden, die gerade so unterschiedlich ist, dass Ihr Programm unerklärlich abstürzt.
Personen, die garantieren möchten, dass ihnen diese Probleme niemals passieren, müssen die Regel befolgen, dass, wenn irgendjemand irgendwo jemals eine Instanz einer bestimmten Klasse für einen bestimmten Typ deklariert hat, keine andere Instanz in einem geschriebenen Programm jemals wieder deklariert werden darf von jemandem. Natürlich gibt es die Problemumgehung
newtype
, eine neue Instanz mit de zu deklarieren, aber das ist immer zumindest eine kleine und manchmal eine große Unannehmlichkeit. In diesem Sinne sind diejenigen, die absichtlich verwaiste Instanzen schreiben, eher unhöflich.Was ist also gegen dieses Problem zu tun? Das Anti-Orphan-Instance-Camp gibt an, dass die GHC-Warnung ein Fehler ist. Es muss sich um einen Fehler handeln, der jeden Versuch, eine Orphan-Instanz zu deklarieren, ablehnt. In der Zwischenzeit müssen wir Selbstdisziplin üben und sie um jeden Preis vermeiden.
Wie Sie gesehen haben, gibt es diejenigen, die sich über diese potenziellen Probleme nicht so viele Sorgen machen. Sie fördern tatsächlich die Verwendung von verwaisten Instanzen als Instrument zur Trennung von Bedenken, wie Sie vorschlagen, und sagen, dass man nur von Fall zu Fall sicherstellen sollte, dass es kein Problem gibt. Die verwaisten Instanzen anderer Leute haben mich oft genug belästigt, um davon überzeugt zu sein, dass diese Haltung zu unbekümmert ist.
Ich denke, die richtige Lösung wäre, dem Importmechanismus von Haskell eine Erweiterung hinzuzufügen, die den Import von Instanzen steuert. Das würde die Probleme nicht vollständig lösen, aber es würde helfen, unsere Programme vor Schäden durch die bereits auf der Welt existierenden verwaisten Instanzen zu schützen. Und dann könnte ich mit der Zeit davon überzeugt sein, dass in bestimmten begrenzten Fällen eine verwaiste Instanz möglicherweise nicht so schlecht ist. (Und genau diese Versuchung ist der Grund, warum einige im Lager gegen Waisen gegen meinen Vorschlag sind.)
Meine Schlussfolgerung aus all dem ist, dass ich zumindest vorerst dringend empfehlen würde, keine Orphan-Instanzen zu deklarieren, um anderen gegenüber rücksichtsvoll zu sein, wenn auch aus keinem anderen Grund. Verwenden Sie a
newtype
.quelle
Gehen Sie voran und unterdrücken Sie diese Warnung!
Sie sind in guter Gesellschaft. Conal macht es in "TypeCompose". "chp-mtl" und "chp-transformers" machen es, "control-monad-exception-mtl" und "control-monad-exception-monadsfd" machen es usw.
Übrigens wissen Sie das wahrscheinlich schon, aber für diejenigen, die es nicht tun und Ihre Frage bei einer Suche stolpern:
Bearbeiten:
Ich erkenne die Probleme, die Yitz in seiner Antwort erwähnte, als echte Probleme an. Ich sehe es jedoch auch als Problem an, verwaiste Instanzen nicht zu verwenden, und ich versuche, das "geringste Übel" auszuwählen, was imho ist, um verwaiste Instanzen umsichtig zu verwenden.
Ich habe in meiner kurzen Antwort nur ein Ausrufezeichen verwendet, weil Ihre Frage zeigt, dass Sie sich der Probleme bereits bewusst sind. Sonst wäre ich weniger begeistert gewesen :)
Ein bisschen Ablenkung, aber was ich glaube, ist die perfekte Lösung in einer perfekten Welt ohne Kompromisse:
Ich glaube, dass die Probleme, die Yitz erwähnt (ohne zu wissen, welche Instanz ausgewählt wurde), in einem "ganzheitlichen" Programmiersystem gelöst werden könnten, in dem:
Zurück aus der Fantasiewelt (oder hoffentlich der Zukunft), jetzt: Ich empfehle, verwaiste Instanzen zu vermeiden, während Sie sie weiterhin verwenden, wenn Sie dies "wirklich brauchen"
quelle
Verwaiste Instanzen sind ein Ärgernis, aber meiner Meinung nach sind sie manchmal notwendig. Ich kombiniere oft Bibliotheken, bei denen ein Typ aus einer Bibliothek und eine Klasse aus einer anderen Bibliothek stammt. Natürlich kann nicht erwartet werden, dass die Autoren dieser Bibliotheken Instanzen für jede denkbare Kombination von Typen und Klassen bereitstellen. Also muss ich sie versorgen, und so sind sie Waisen.
Die Idee, dass Sie den Typ in einen neuen Typ einschließen sollten, wenn Sie eine Instanz bereitstellen müssen, ist eine Idee mit theoretischem Wert, aber unter vielen Umständen einfach zu langweilig. Es ist die Art von Idee, die von Leuten vorgebracht wird, die keinen Haskell-Code für ihren Lebensunterhalt schreiben. :) :)
Stellen Sie also verwaiste Instanzen bereit. Sie sind harmlos.
Wenn Sie ghc mit verwaisten Instanzen zum Absturz bringen können, ist dies ein Fehler und sollte als solcher gemeldet werden. (Der Fehler, den ghc hatte / hat, mehrere Instanzen nicht zu erkennen, ist nicht so schwer zu beheben.)
Beachten Sie jedoch, dass in Zukunft möglicherweise jemand anderes die bereits vorhandene Instanz hinzufügt und möglicherweise ein Fehler (Kompilierungszeit) auftritt.
quelle
(Ord k, Arbitrary k, Arbitrary v) ⇒ Arbitrary (Map k v)
Verwendung von QuickCheck.In diesem Fall denke ich, dass die Verwendung von verwaisten Instanzen in Ordnung ist. Die allgemeine Faustregel für mich lautet: Sie können eine Instanz definieren, wenn Sie die Typklasse "besitzen" oder wenn Sie den Datentyp (oder eine Komponente davon "besitzen" - dh eine Instanz für Vielleicht ist MyData auch in Ordnung. zumindest manchmal). Innerhalb dieser Einschränkungen ist es Ihr eigenes Geschäft, wo Sie sich entscheiden, die Instanz zu platzieren.
Es gibt noch eine weitere Ausnahme: Wenn Sie weder die Typklasse noch den Datentyp besitzen, aber eine Binärdatei und keine Bibliothek erstellen, ist das auch in Ordnung.
quelle
(Ich weiß, dass ich zu spät zur Party komme, aber dies kann für andere immer noch nützlich sein.)
Sie können die verwaisten Instanzen in ihrem eigenen Modul behalten. Wenn jemand dieses Modul importiert, liegt dies speziell daran, dass er sie benötigt, und er kann den Import vermeiden, wenn er Probleme verursacht.
quelle
In diesem Sinne verstehe ich die Position der WRT-Bibliotheken des Anti-Orphan-Instance-Camps, aber für ausführbare Ziele sollten Orphan-Instanzen nicht in Ordnung sein?
quelle