Antworten:
Die Verwendung von Maybe(oder seines Cousins, Eitherder im Wesentlichen auf die gleiche Weise funktioniert, Sie jedoch anstelle von einen beliebigen Wert zurückgeben lässt Nothing) dient einem etwas anderen Zweck als Ausnahmen. In Java ist das so, als hätte man eher eine aktivierte Ausnahme als eine Laufzeitausnahme. Es steht für etwas Erwartetes, mit dem Sie sich befassen müssen, und nicht für einen Fehler, den Sie nicht erwartet haben.
Eine Funktion wie indexOfwürde also einen MaybeWert zurückgeben, da Sie die Möglichkeit erwarten, dass das Element nicht in der Liste enthalten ist. Dies ähnelt der Rückkehr nullvon einer Funktion, außer auf typsichere Weise, die Sie dazu zwingt, sich mit dem nullFall zu befassen . EitherDies funktioniert auf die gleiche Weise, mit der Ausnahme, dass Sie Informationen zurückgeben können, die mit dem Fehlerfall verknüpft sind, sodass sie einer Ausnahme ähnlicher sind als Maybe.
Was sind die Vorteile des Maybe/ EitherAnsatzes? Zum einen ist es ein erstklassiger Bürger der Sprache. Vergleichen wir eine Funktion mit einer Funktion, Eitherdie eine Ausnahme auslöst. Für den Ausnahmefall ist Ihre einzige wirkliche Möglichkeit eine try...catchAussage. Für die EitherFunktion können Sie vorhandene Kombinatoren verwenden, um die Flusssteuerung übersichtlicher zu gestalten. Hier einige Beispiele:
Nehmen wir zunächst an, Sie möchten mehrere Funktionen ausprobieren, die nacheinander fehlschlagen können, bis Sie eine erhalten, die dies nicht tut. Wenn Sie keine ohne Fehler erhalten, möchten Sie eine spezielle Fehlermeldung zurückgeben. Dies ist eigentlich ein sehr nützliches Muster, aber es wäre ein schrecklicher Schmerz try...catch. Glücklicherweise können Sie, da Eitheres sich nur um einen normalen Wert handelt, vorhandene Funktionen verwenden, um den Code viel klarer zu gestalten:
firstThing <|> secondThing <|> throwError (SomeError "error message")
Ein weiteres Beispiel ist eine optionale Funktion. Angenommen, Sie müssen mehrere Funktionen ausführen, darunter eine, die versucht, eine Abfrage zu optimieren. Wenn dies fehlschlägt, möchten Sie, dass alles andere ausgeführt wird. Sie könnten Code schreiben wie:
do a <- getA
b <- getB
optional (optimize query)
execute query a b
Beide Fälle sind klarer und kürzer als die Verwendung try..catchund vor allem semantischer. Wenn Sie eine Funktion wie <|>oder verwenden, werden optionalIhre Absichten viel klarer, als wenn Sie try...catchimmer Ausnahmen behandeln.
Beachten Sie auch , dass Sie nicht tun zu Wurf haben Sie den Code mit Zeilen wie if a == Nothing then Nothing else ...! Der springende Punkt bei der Behandlung Maybeund Eitherals Monade ist, dies zu vermeiden. Sie können die Ausbreitungssemantik in die Bindefunktion kodieren, sodass Sie die Null- / Fehlerprüfungen kostenlos erhalten. Das einzige Mal, dass Sie explizit prüfen müssen, ist, ob Sie etwas anderes als ein Nothinggegebenes zurückgeben möchten Nothing, und selbst dann ist es einfach: Es gibt eine Reihe von Standard-Bibliotheksfunktionen, die diesen Code schöner machen.
Ein weiterer Vorteil ist, dass ein Maybe/ Either-Typ nur einfacher ist. Es ist nicht erforderlich, die Sprache um zusätzliche Schlüsselwörter oder Kontrollstrukturen zu erweitern - alles ist nur eine Bibliothek. Da es sich nur um normale Werte handelt, wird das Typensystem einfacher - in Java müssen Sie zwischen Typen (z. B. dem Rückgabetyp) und Effekten (z. B. throwsAnweisungen) unterscheiden, die Sie nicht verwenden würden Maybe. Sie verhalten sich ebenso wie alle anderen benutzerdefinierten Typen - es ist kein spezieller Fehlerbehandlungscode erforderlich, der in die Sprache eingebettet ist.
Ein weiterer Sieg ist , dass Maybe/ Eithersind functors und Monaden, welche Mittel sie die Vorteile der bestehenden Monade Ablaufsteuerungsfunktionen übernehmen kann (von denen es eine ganze Reihe) und im Allgemeinen spielen schön zusammen mit anderen Monaden.
Das heißt, es gibt einige Einschränkungen. Zum einen werden ungeprüfte Ausnahmen weder ersetzt Maybenoch Eitherersetzt . Sie möchten eine andere Möglichkeit, um mit Dingen wie dem Teilen durch 0 umzugehen, einfach, weil es ein Problem wäre, wenn jede einzelne Division einen Wert zurückgibt.Maybe
Ein weiteres Problem besteht darin, dass mehrere Arten von Fehlern zurückgegeben werden (dies gilt nur für Either). Mit Ausnahmen können Sie verschiedene Arten von Ausnahmen in derselben Funktion auslösen. mit Eitherbekommst du nur einen typ. Dies kann durch Subtypisierung oder ein ADT überwunden werden, das alle Arten von Fehlern als Konstruktoren enthält (dieser zweite Ansatz wird normalerweise in Haskell verwendet).
Trotzdem bevorzuge ich den Maybe/ Either-Ansatz, weil ich ihn einfacher und flexibler finde.
OpenFile()kann werfen FileNotFoundoder NoPermissionoder TooManyDescriptorsusw. A Keine trägt diese Information nicht.if None return None-Stil-Anweisungen.Vor allem haben eine Ausnahme und eine Vielleicht-Monade unterschiedliche Zwecke - eine Ausnahme wird verwendet, um ein Problem zu kennzeichnen, eine Vielleicht-Monade nicht.
"Schwester, wenn ein Patient in Raum 5 ist, können Sie ihn bitten zu warten?"
(Beachten Sie das "Wenn" - dies bedeutet, dass der Arzt eine Vielleicht-Monade erwartet. )
NoneWerte einfach weitergegeben werden). Ihr Punkt 5 ist nur irgendwie richtig… die Frage ist: Welche Situationen sind eindeutig außergewöhnlich? Wie sich herausstellt ... nicht viele .
bind, dass das Testen auf Nonekeinen syntaktischen Aufwand entsteht. Ein sehr einfaches Beispiel: C # überlastet die NullableOperatoren nur angemessen. NoneAuch bei Verwendung des Typs ist keine Überprüfung erforderlich. Natürlich wird die Prüfung noch durchgeführt (es ist typsicher), aber hinter den Kulissen und nicht Ihren Code überladen. Das Gleiche gilt in gewissem Sinne für Ihren Einwand gegen meinen Einwand gegen (5), aber ich bin damit einverstanden, dass er möglicherweise nicht immer zutrifft.
Maybeals Monade darin, die Ausbreitung Noneimplizit zu machen . Dies bedeutet, dass Sie, wenn Sie einen Nonebestimmten NoneCode zurückgeben möchten , überhaupt keinen speziellen Code schreiben müssen. Sie müssen nur übereinstimmen, wenn Sie etwas Besonderes tun möchten None. Sie brauchen nie eine if None then NoneArt von Aussagen.
nullgenau so (zB if Nothing then Nothing) kostenlosMaybe checkt, weil es eine Monade ist. Es ist in der Definition von bind ( >>=) für codiert Maybe.
Either), die sich genau so verhalten Maybe. Das Umschalten zwischen den beiden ist eigentlich ziemlich einfach, da Maybees sich eigentlich nur um einen Sonderfall handelt Either. (In Haskell, könnte man sich vorstellen , Maybewie Either ().)
"Vielleicht" ist kein Ersatz für Ausnahmen. Ausnahmen sollen in Ausnahmefällen verwendet werden (zum Beispiel: Öffnen einer DB-Verbindung und der DB-Server ist nicht da, obwohl es sein sollte). "Vielleicht" dient zum Modellieren einer Situation, in der Sie möglicherweise einen gültigen Wert haben oder nicht. Angenommen, Sie erhalten einen Wert aus einem Wörterbuch für einen Schlüssel: Er kann vorhanden sein oder nicht - an keinem dieser Ergebnisse ist etwas "Außergewöhnliches".
Ich stimme Tikhons Antwort zu, aber ich denke, es gibt einen sehr wichtigen praktischen Punkt, den jeder vermisst:
EitherMechanismus ist überhaupt nicht an Threads gekoppelt.Was wir heutzutage im wirklichen Leben sehen, ist, dass viele asynchrone Programmierlösungen eine Variante des EitherStils der Fehlerbehandlung verwenden. Betrachten Sie Javascript- Versprechen , wie in einem dieser Links beschrieben:
Das Konzept der Versprechungen ermöglicht es Ihnen, wie folgt asynchronen Code zu schreiben (entnommen aus dem letzten Link):
var greetingPromise = sayHello();
greetingPromise
.then(addExclamation)
.then(function (greeting) {
console.log(greeting); // 'hello world!!!!’
}, function(error) {
console.error('uh oh: ', error); // 'uh oh: something bad happened’
});
Grundsätzlich ist ein Versprechen ein Objekt, das:
Da die systemeigene Ausnahmebedingungsunterstützung der Sprache nicht funktioniert, wenn Ihre Berechnung über mehrere Threads hinweg erfolgt, muss eine versprochene Implementierung einen Fehlerbehandlungsmechanismus bereitstellen, der sich als Monaden herausstellt, die denen von Haskell Maybe/ Eithertypes ähneln .
Das System vom Typ Haskell fordert den Benutzer auf, die Möglichkeit von a zu bestätigen Nothing, wohingegen Programmiersprachen häufig nicht erfordern, dass eine Ausnahme abgefangen wird. Das bedeutet, dass wir zur Kompilierungszeit wissen, dass der Benutzer nach einem Fehler gesucht hat.
throws NPEjeder einzelnen Signatur und catch(...) {throw ...} jedem einzelnen Methodenkörper ein hinzufügen müssen . Aber ich glaube, es gibt einen Markt für Überprüfungen in demselben Sinne wie für Vielleicht: Die Nullwertfähigkeit ist optional und wird im Typsystem nachverfolgt.
Die Vielleicht-Monade ist im Grunde die gleiche wie die Prüfung mit "Null bedeutet Fehler" in den meisten gängigen Sprachen (mit der Ausnahme, dass die Null geprüft werden muss) und weist im Wesentlichen dieselben Vor- und Nachteile auf.
MaybeZahlen durch Schreiben hinzufügen , a + b ohne nachsehen zu müssen None, und das Ergebnis ist wieder ein optionaler Wert.
Maybe Typ , aber die Verwendung Maybeals Monade fügt Syntaxzucker hinzu, mit dem sich nullartige Logik viel eleganter ausdrücken lässt.
Die Ausnahmebehandlung kann ein echtes Problem beim Factoring und Testen sein. Ich weiß, dass Python eine nette "with" -Syntax bietet, mit der Sie Ausnahmen ohne den starren "try ... catch" -Block abfangen können. Aber in Java zum Beispiel sind catch-Blöcke groß, ausführlich oder extrem ausführlich und schwer zu trennen. Darüber hinaus fügt Java das gesamte Rauschen um aktivierte und nicht aktivierte Ausnahmen hinzu.
Wenn Ihre Monade stattdessen Ausnahmen abfängt und sie als eine Eigenschaft des monadischen Raums behandelt (anstelle von Verarbeitungsfehlern), können Sie Funktionen, die Sie in diesen Raum einbinden, unabhängig davon, was sie werfen oder abfangen, mischen und zuordnen.
Wenn, noch besser, Ihre Monade Bedingungen verhindert, in denen Ausnahmen auftreten könnten (z. B. ein Null-Check in Vielleicht), dann noch besser. wenn ... dann ist es viel einfacher zu faktorisieren und zu testen als zu versuchen ... zu fangen.
Wie ich gesehen habe, verfolgt Go einen ähnlichen Ansatz, indem angegeben wird, dass jede Funktion zurückgibt (Antwort, Fehler). Das ist in etwa so, als würde man die Funktion in einen Monadenraum "heben", in dem der Kernantworttyp mit einer Fehleranzeige versehen ist, und Ausnahmen effektiv ausweichen und abfangen.