Ich brauche Hilfe, um die Verwendung der drei Haskell-Funktionen zu verstehen
- try (
Control.Exception.try :: Exception e => IO a -> IO (Either e a)
) - catch (
Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a
) - handle (
Control.Exception.handle :: Exception e => (e -> IO a) -> IO a -> IO a
)
Ich muss verschiedene Dinge wissen:
- Wann benutze ich welche Funktion?
- Wie verwende ich diese Funktion anhand eines einfachen Beispiels?
- Wo ist der Unterschied zwischen Fang und Griff? Sie haben fast die gleiche Unterschrift nur mit einer anderen Reihenfolge.
Ich werde versuchen, meine Prüfungen aufzuschreiben und hoffe, dass Sie mir helfen können:
Versuchen
Ich habe ein Beispiel wie:
x = 5 `div` 0
test = try (print x) :: IO (Either SomeException ())
Ich habe zwei Fragen:
Wie kann ich eine benutzerdefinierte Fehlerausgabe festlegen?
Was kann ich tun, um alle Fehler auf SomeException zu setzen, damit ich das nicht schreiben muss?
:: IO (Either SomeException())
fangen / versuchen
Können Sie mir ein kurzes Beispiel mit einer benutzerdefinierten Fehlerausgabe zeigen?
haskell
exception-handling
develhevel
quelle
quelle
Antworten:
Wann benutze ich welche Funktion?
Hier ist die Empfehlung aus der Control.Exception-Dokumentation:
finally
,bracket
oderonException
.try
Familienmitglied zu verwenden.catch
odercatchJust
.try :: Exception e => IO a -> IO (entweder ea)
try
führt eineIO
auszuführende Aktion aus und gibt eine zurückEither
. Wenn die Berechnung erfolgreich war, wird das Ergebnis in einenRight
Konstruktor eingeschlossen. (Denken Sie richtig statt falsch). Wenn die Aktion eine Ausnahme des angegebenen Typs auslöste , wird sie in einemLeft
Konstruktor zurückgegeben. Wenn die Ausnahme nicht vom entsprechenden Typ war, wird sie den Stapel weiter ausbreiten. Wenn SieSomeException
als Typ angeben, werden alle Ausnahmen abgefangen , die möglicherweise eine gute Idee sind oder nicht.Beachten Sie, dass Sie, wenn Sie eine Ausnahme von einer reinen Berechnung abfangen möchten, die
evaluate
Auswertung innerhalb der erzwingen müssentry
.main = do result <- try (evaluate (5 `div` 0)) :: IO (Either SomeException Int) case result of Left ex -> putStrLn $ "Caught exception: " ++ show ex Right val -> putStrLn $ "The answer was: " ++ show val
catch :: Exception e => IO a -> (e -> IO a) -> IO a
catch
ist ähnlich wietry
. Zuerst wird versucht, die angegebeneIO
Aktion auszuführen. Wenn jedoch eine Ausnahme ausgelöst wird, erhält der Handler die Ausnahme, um eine alternative Antwort zu erhalten.main = catch (print $ 5 `div` 0) handler where handler :: SomeException -> IO () handler ex = putStrLn $ "Caught exception: " ++ show ex
Es gibt jedoch einen wichtigen Unterschied. Bei Verwendung
catch
Ihres Handlers kann nicht durch eine asynchrone Ausnahme unterbrochen werden (dh von einem anderen Thread über ausgelöstthrowTo
). Versuche, eine asynchrone Ausnahme auszulösen, werden blockiert, bis Ihr Handler die Ausführung beendet hat.Beachten Sie, dass es
catch
im Prelude einen anderen gibt , also möchten Sie vielleicht etwas tunimport Prelude hiding (catch)
.handle :: Exception e => (e -> IO a) -> IO a -> IO a
handle
ist einfachcatch
mit den Argumenten in umgekehrter Reihenfolge. Welche verwendet werden soll, hängt davon ab, was Ihren Code lesbarer macht oder welche besser passt, wenn Sie eine Teilanwendung verwenden möchten. Sie sind ansonsten identisch.tryJust, catchJust und handleJust
Beachten Sie, dass
try
,catch
undhandle
wird fangen alle Ausnahmen von der angegebenen / gefolgert Typ.tryJust
Mit und Freunden können Sie eine Auswahlfunktion angeben, die herausfiltert, welche Ausnahmen Sie speziell behandeln möchten. Beispielsweise sind alle arithmetischen Fehler vom TypArithException
. Wenn Sie nur fangen wollenDivideByZero
, können Sie:main = do result <- tryJust selectDivByZero (evaluate $ 5 `div` 0) case result of Left what -> putStrLn $ "Division by " ++ what Right val -> putStrLn $ "The answer was: " ++ show val where selectDivByZero :: ArithException -> Maybe String selectDivByZero DivideByZero = Just "zero" selectDivByZero _ = Nothing
Ein Hinweis zur Reinheit
Beachten Sie, dass diese Art der Ausnahmebehandlung nur in unreinem Code (dh der
IO
Monade) auftreten kann. Wenn Sie Fehler in reinem Code behandeln müssen, sollten Sie prüfen, ob Werte mitMaybe
oderEither
stattdessen (oder einem anderen algebraischen Datentyp) zurückgegeben werden. Dies ist oft vorzuziehen, da es expliziter ist, sodass Sie immer wissen, was wo passieren kann. Monaden wieControl.Monad.Error
erleichtern die Arbeit mit dieser Art der Fehlerbehandlung.Siehe auch:
quelle
try
, es sei denn, Sie erholen sich von einer asynchronen Ausnahme, in diesem Fall verwendencatch
"Edward Z. Yang hat einen Artikel über die Ausnahmebehandlung in Haskell: 8 Möglichkeiten, Fehler in Haskell zu melden, überarbeitet .
quelle
Ich sehe, dass eine Sache, die dich auch nervt (deine zweite Frage), das Schreiben von
:: IO (Either SomeException ())
und es hat mich auch geärgert.Ich habe jetzt einen Code geändert:
let x = 5 `div` 0 result <- try (print x) :: IO (Either SomeException ()) case result of Left _ -> putStrLn "Error" Right () -> putStrLn "OK"
Dazu:
let x = 5 `div` 0 result <- try (print x) case result of Left (_ :: SomeException) -> putStrLn "Error" Right () -> putStrLn "OK"
Dazu müssen Sie die
ScopedTypeVariables
GHC-Erweiterung verwenden, aber ich denke, es lohnt sich ästhetisch.quelle
Betreff : Frage 3: Fang und Handle sind gleich (über hoogle gefunden ). Die Wahl der Verwendung hängt normalerweise von der Länge der einzelnen Argumente ab. Wenn die Aktion kürzer ist, verwenden Sie catch und umgekehrt. Einfaches Handle-Beispiel aus der Dokumentation:
do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ...
Es ist auch denkbar, dass Sie die Grifffunktion Curry machen, um einen benutzerdefinierten Handler zu erstellen, den Sie dann weitergeben können, z. (aus der Dokumentation übernommen):
let handler = handle (\NonTermination -> exitWith (ExitFailure 1))
Benutzerdefinierte Fehlermeldungen:
do let result = 5 `div` 0 let handler = (\_ -> print "Error") :: IOException -> IO () catch (print result) handler
quelle