Okay, erste Regel zur Fehlerbehandlung in Haskell: Niemals verwendenerror
.
Es ist einfach in jeder Hinsicht schrecklich. Es existiert nur als Akt der Geschichte und die Tatsache, dass Prelude es benutzt, ist schrecklich. Benutze es nicht.
Die einzig denkbare Zeit, die Sie nutzen könnten, ist, wenn etwas so schrecklich ist, dass etwas mit dem eigentlichen Realitätsgefüge nicht in Einklang stehen muss, wodurch das Ergebnis Ihres Programms in Frage gestellt wird.
Nun ist die Frage wird Maybe
vs Either
. Maybe
eignet sich gut für etwas head
, das einen Wert zurückgeben kann oder nicht, aber es gibt nur einen möglichen Grund für das Scheitern. Nothing
sagt so etwas wie "es ist kaputt gegangen und du weißt schon warum". Einige würden sagen, es deutet auf eine Teilfunktion hin.
Die robusteste Form der Fehlerbehandlung ist Either
+ ein Fehler-ADT.
Zum Beispiel habe ich in einem meiner Hobby-Compiler so etwas wie
data CompilerError = ParserError ParserError
| TCError TCError
...
| ImpossibleError String
data ParserError = ParserError (Int, Int) String
data TCError = CouldntUnify Ty Ty
| MissingDefinition Name
| InfiniteType Ty
...
type ErrorM m = ExceptT CompilerError m -- from MTL
Jetzt definiere ich eine Reihe von Fehlertypen und verschachtele sie so, dass ich einen herrlichen Fehler der obersten Ebene habe. Dies kann ein Fehler in jeder Phase der Kompilierung sein oder ein Fehler ImpossibleError
, der auf einen Compiler-Fehler hinweist.
Bei jedem dieser Fehlertypen wird versucht, so lange wie möglich Informationen für hübsches Drucken oder andere Analysen zu speichern. Noch wichtiger ist, dass ich durch das Fehlen eines Strings testen kann, dass das Ausführen eines falsch geschriebenen Programms durch die Typprüfung tatsächlich einen Vereinigungsfehler erzeugt! Sobald etwas a ist String
, ist es für immer verschwunden und alle darin enthaltenen Informationen sind für den Compiler / die Tests undurchsichtig, daher Either String
ist es auch nicht großartig.
Schließlich packe ich diesen Typ in ExceptT
einen neuen Monadentransformator von MTL. Dies ist im Wesentlichen EitherT
und kommt mit einer schönen Reihe von Funktionen zum Werfen und Auffangen von Fehlern auf eine reine, angenehme Art und Weise.
Schließlich ist zu erwähnen, dass Haskell über die Mechanismen verfügt, mit denen Ausnahmen wie in anderen Sprachen behandelt werden können IO
. Ich weiß, dass manche Leute diese gern für IO
schwere Anwendungen verwenden, bei denen möglicherweise alles versagt, aber so selten, dass sie nicht gerne darüber nachdenken. Ob Sie diese unreinen Ausnahmen verwenden oder einfach nur, ExceptT Error IO
ist Geschmackssache. Ich persönlich entscheide mich dafür, ExceptT
weil ich gerne an die Möglichkeit eines Scheiterns erinnert werde.
Zusammenfassend
Maybe
- Ich kann auf eine offensichtliche Weise scheitern
Either CustomType
- Ich kann scheitern, und ich sage Ihnen, was passiert ist
IO
+ Ausnahmen - Ich scheitere manchmal. Überprüfen Sie meine Dokumente, um zu sehen, was ich bei der Arbeit werfe
error
- Ich hasse dich auch User
head
oderlast
so gut zu sein scheinen.error
Ich habe mich gefragt, ob das wirklich eine gute Möglichkeit ist, Dinge zu tun, und ich habe einfach etwas verpasst. Dies beantwortet diese Frage. :)