Die Art der Control.Exception.handle
ist:
handle :: Exception e => (e -> IO a) -> IO a -> IO a
Das Problem, das Sie sehen, ist, dass der Lambda-Ausdruck (\_ -> return "err")
nicht vom Typ ist e -> IO a
wo e
eine Instanz von Exception
ist. Klar wie Schlamm? Gut. Jetzt werde ich eine Lösung bereitzustellen, die tatsächlich nützlich sein sollte :)
Es ist einfach so in Ihrem Fall kommt vor, dass e
Control.Exception.ErrorCall
sein sollte, da undefined
verwendet error
die ErrorCall
(eine Instanz von Exception
) wirft.
Verwendungen undefined
behandeln Sie so etwas wie handleError
definieren:
handleError :: (ErrorCall -> IO a) -> IO a -> IO a
handleError = handle
Es ist im Wesentlichen ein Alias Control.Exception.handle
mit e
fixiert, wie ErrorCall
das ist, was error
wirft.
Es sieht aus wie diese ausgeführt werden, wenn in GHCi 7.4.1:
ghci> handleError (\_ -> return "err") undefined
"err"
Um alle Ausnahmen eine handleAll
Funktion behandeln kann geschrieben werden wie folgt:
handleAll :: (SomeException -> IO a) -> IO a -> IO a
handleAll = handle
alle Ausnahmen Beschäftigten hat Folgen beschrieben gut in diesem Auszug der Control.Exception
Dokumentation:
Fangen alle Ausnahmen
Es ist möglich, alle Ausnahmen zu fangen, indem sie die Art SomeException
mit:
catch f (\e -> ... (e :: SomeException) ...)
Dies ist jedoch normalerweise nicht das, was Sie tun möchten!
Angenommen, Sie möchten eine Datei lesen, aber wenn sie nicht existiert, fahren Sie fort, als ob sie ""
enthielte. Sie könnten versucht sein, alle Ausnahmen abzufangen und ""
im Handler zurückgeben. Dies hat jedoch alle möglichen unerwünschten Folgen. Wenn der Benutzer beispielsweise control-C genau zum richtigen Zeitpunkt drückt, wird die Ausnahme UserInterrupt
abgefangen, und das Programm wird unter der Annahme fortgesetzt, dass die Datei ""
enthält. Wenn ein anderer Thread versucht, den Thread zu beenden, der die Datei liest, wird die Ausnahme ThreadKilled
ebenfalls ignoriert.
Stattdessen sollten Sie nur genau die Ausnahmen abfangen, die Sie wirklich wollen. In diesem Fall wäre dies wahrscheinlich spezifischer als selbst "irgendeine IO-Ausnahme"; Ein Berechtigungsfehler würde wahrscheinlich auch anders behandelt werden. Stattdessen würden Sie wahrscheinlich so etwas wie wollen:
e <- tryJust (guard . isDoesNotExistError) (readFile f)
let str = either (const "") id e
Es gibt Gelegenheiten, wenn Sie wirklich jede Art von Ausnahme brauchen zu fangen. In den meisten Fällen ist dies jedoch nur so, dass Sie etwas aufräumen können; Sie sind nicht wirklich an der Ausnahme selbst interessiert.Wenn Sie beispielsweise eine Datei öffnen, möchten Sie sie erneut schließen, unabhängig davon, ob die Verarbeitung der Datei normal ausgeführt wird oder eine Ausnahme auslöst. In diesen Fällen können Sie jedoch Funktionen wie bracket
, finally
und onException
verwenden, die Ihnen niemals die Ausnahme übergeben, sondern nur die Bereinigungsfunktionen an den entsprechenden Stellen aufrufen.
Aber manchmal müssen Sie wirklich irgendeine Ausnahme fangen, und tatsächlich sehen, was die Ausnahme ist. Ein Beispiel ist auf der obersten Ebene eines Programms, möchten Sie möglicherweise eine Ausnahme abfangen, drucken Sie es in eine Protokolldatei oder den Bildschirm, und dann ordnungsgemäß beendet. In diesen Fällen können Sie catch
(oder eine der anderen ausnahmebedingten Funktionen) mit dem Typ SomeException
verwenden.
Quelle: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#g:4
Warum der Compiler wissen müssen die Ausnahme? Die Funktion behandelt jeden Typ aus der Klasse. – luntain
Wegen der Art von 'handle'; Jede Verwendung von 'handle' muss auf einen bestimmten Typ aus der Exception-Klasse angewendet werden. Da Ihr Handler für alle Typen in der Klasse arbeitet, gibt es keine Möglichkeit für den Compiler, dem Handle einen Typ zuzuweisen. (Das 'e' kommt vom Typ von' handle'.) –
Wenn Sie die ScopedTypeVariables-Erweiterung verwenden, können Sie einfach 'handle (\ (_ :: SomeException) -> return "err") undefined'. – porges