J’ai besoin d’aide pour comprendre l’utilisation des trois fonctions Haskell
Control.Exception.try :: Exception e => IO a -> IO (Either ea)
) Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a
) Control.Exception.handle :: Exception e => (e -> IO a) -> IO a -> IO a
) J’ai besoin de savoir plusieurs choses:
Je vais essayer d’écrire mes essais et j’espère que vous pourrez m’aider:
essayer
J’ai un exemple comme:
x = 5 `div` 0 test = try (print x) :: IO (Either SomeException ())
J’ai deux questions:
Comment puis-je définir une sortie d’erreur personnalisée?
Que puis-je faire pour définir toutes les erreurs sur SomeException alors je ne dois pas écrire le :: IO (Either SomeException())
attraper / essayer
Pouvez-vous me montrer un court exemple avec une sortie d’erreur personnalisée?
Voici la recommandation de la documentation Control.Exception:
onException
, utilisez finally
, bracket
ou onException
. try
. catch
ou catchJust
. try
exécute une action IO
à exécuter et renvoie un Either
. Si le calcul a réussi, le résultat est donné enveloppé dans un constructeur Right
. (Pensez bien par opposition à mal). Si l’action a généré une exception du type spécifié , elle est renvoyée dans un constructeur Left
. Si l’exception n’est pas du type approprié, elle continue à se propager dans la stack. Spécifier SomeException
comme type interceptera toutes les exceptions, ce qui peut être ou ne pas être une bonne idée.
Notez que si vous voulez intercepter une exception d’un calcul pur, vous devrez utiliser la fonction d’ evaluate
pour forcer l’évaluation dans l’ try
.
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
est similaire à try
. Il essaie d’abord d’exécuter l’action IO
spécifiée, mais si une exception est levée, le gestionnaire reçoit une exception pour obtenir une autre réponse.
main = catch (print $ 5 `div` 0) handler where handler :: SomeException -> IO () handler ex = putStrLn $ "Caught exception: " ++ show ex
Cependant, il y a une différence importante. Lors de l’utilisation de catch
votre gestionnaire ne peut pas être interrompu par une exception asynchrone (c’est-à-dire lancée depuis un autre thread via throwTo
). Toute tentative de générer une exception asynchrone bloquera jusqu’à la fin de l’exécution de votre gestionnaire.
Notez qu’il existe une autre catch
dans le prélude, vous pouvez donc import Prelude hiding (catch)
.
handle
est simplement catch
aux arguments dans l’ordre inverse. La méthode à utiliser dépend de ce qui rend votre code plus lisible ou de celui qui convient le mieux si vous souhaitez utiliser une application partielle. Ils sont par ailleurs identiques.
Notez que try
, catch
et handle
intercepte toutes les exceptions du type spécifié / déduit. tryJust
et friends vous permettent de spécifier une fonction de sélection qui filtre les exceptions que vous souhaitez gérer spécifiquement. Par exemple, toutes les erreurs arithmétiques sont de type ArithException
. Si vous voulez seulement attraper DivideByZero
, vous pouvez faire:
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 Ssortingng selectDivByZero DivideByZero = Just "zero" selectDivByZero _ = Nothing
Notez que ce type de gestion des exceptions ne peut se produire que dans un code impur (c’est-à-dire la monade IO
). Si vous avez besoin de gérer des erreurs dans du code pur, vous devriez envisager de renvoyer des valeurs en utilisant Maybe
ou Either
place (ou un autre type de données algébrique). Ceci est souvent préférable car il est plus explicite et vous savez toujours ce qui peut arriver où. Les monades comme Control.Monad.Error
facilitent l’utilisation de ce type de gestion des erreurs.
Voir également:
Edward Z. Yang a un article sur la gestion des exceptions dans haskell: 8 façons de signaler les erreurs dans Haskell revisitées .
Re: question 3: attraper et manipuler sont les mêmes (grâce à hoogle ). Le choix de l’utilisation dépendra généralement de la longueur de chaque argument. Si l’action est plus courte, utilisez catch et vice versa. Exemple de poignée simple de la documentation:
do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ...
En outre, vous pourriez peut-être créer une fonction de gestion pour créer un gestionnaire personnalisé, que vous pouvez ensuite transmettre, par exemple. (adapté de la documentation):
let handler = handle (\NonTermination -> exitWith (ExitFailure 1))
Messages d’erreur personnalisés:
do let result = 5 `div` 0 let handler = (\_ -> print "Error") :: IOException -> IO () catch (print result) handler
Je vois qu’une chose qui vous agace aussi (votre deuxième question) est l’écriture de :: IO (Either SomeException ())
et cela m’a agacé aussi.
J’ai changé du code maintenant de ceci:
let x = 5 `div` 0 result <- try (print x) :: IO (Either SomeException ()) case result of Left _ -> putStrLn "Error" Right () -> putStrLn "OK"
Pour ça:
let x = 5 `div` 0 result <- try (print x) case result of Left (_ :: SomeException) -> putStrLn "Error" Right () -> putStrLn "OK"
Pour ce faire, vous devez utiliser l’extension ScopedTypeVariables
GHC, mais je pense que cela en vaut esthétiquement.