¿Cuándo uso qué función?
Aquí está la recomendación de la documentación de Control.Exception:
- Si usted quiere hacer algo de limpieza en caso de que se produce una excepción, utilizar
finally
, bracket
o onException
.
- Para recuperarse después de una excepción y hacer otra cosa, la mejor opción es utilizar uno de la
try
familia.
- ... a menos que se esté recuperando de una excepción asincrónica, en cuyo caso use
catch
o catchJust
.
intente :: Excepción e => IO a -> IO (cualquiera de los dos)
try
realiza una IO
acción para ejecutarse y devuelve un Either
. Si el cálculo tuvo éxito, el resultado se entrega envuelto en un Right
constructor. (Piense bien en lugar de mal). Si la acción arrojó una excepción del tipo especificado , se devuelve en un Left
constructor. Si la excepción no fue del tipo apropiado, continúa propagándose hacia arriba en la pila. Especificar SomeException
como tipo detectará todas las excepciones, lo que puede ser una buena idea o no.
Tenga en cuenta que si desea capturar una excepción de un cálculo puro, tendrá que usar evaluate
para forzar la evaluación dentro de 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 :: Exception e => IO a -> (e -> IO a) -> IO a
catch
es similar a try
. Primero intenta ejecutar la IO
acción especificada , pero si se lanza una excepción, el controlador recibe la excepción para obtener una respuesta alternativa.
main = catch (print $ 5 `div` 0) handler
where
handler :: SomeException -> IO ()
handler ex = putStrLn $ "Caught exception: " ++ show ex
Sin embargo, existe una diferencia importante. Cuando se usa, catch
su controlador no puede ser interrumpido por una excepción asincrónica (es decir, arrojado desde otro hilo a través de throwTo
). Los intentos de generar una excepción asincrónica se bloquearán hasta que su controlador haya terminado de ejecutarse.
Tenga en cuenta que hay algo diferente catch
en el Preludio, por lo que es posible que desee hacerlo import Prelude hiding (catch)
.
handle :: Exception e => (e -> IO a) -> IO a -> IO a
handle
es simplemente catch
con los argumentos en orden inverso. Cuál usar depende de lo que hace que su código sea más legible, o cuál encaja mejor si desea usar una aplicación parcial. Por lo demás, son idénticos.
tryJust, catchJust y handleJust
Tenga en cuenta que try
, catch
y handle
cogerá todas las excepciones del tipo especificado / inferido. tryJust
y amigos le permiten especificar una función de selección que filtra qué excepciones desea manejar específicamente. Por ejemplo, todos los errores aritméticos son de tipo ArithException
. Si solo quiere atrapar DivideByZero
, puede hacer:
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
Una nota sobre la pureza
Tenga en cuenta que este tipo de manejo de excepciones solo puede ocurrir en código impuro (es decir, la IO
mónada). Si necesita manejar errores en código puro, debería considerar devolver valores usando Maybe
o en su Either
lugar (o algún otro tipo de datos algebraicos). Esto suele ser preferible, ya que es más explícito para que siempre sepa qué puede suceder y dónde. Monads like Control.Monad.Error
hace que este tipo de manejo de errores sea más fácil de trabajar.
Ver también: