2011-05-15 11 views
65

necesito ayuda para entender el uso de los tres Haskell Excepción funcionamanejo en Haskell

  • intento (Control.Exception.try :: Exception e => IO a -> IO (Either e a))
  • captura (Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a)
  • mango (Control.Exception.handle :: Exception e => (e -> IO a) -> IO a -> IO a)

I necesita saber varias cosas:

  1. ¿Cuándo uso qué función?
  2. ¿Cómo uso esta función con un ejemplo simple?
  3. ¿Dónde está la diferencia entre catch y handle? Tienen casi la misma firma solo con un orden diferente.

voy a tratar de escribir mis ensayos y espero que me puedan ayudar:

tratar

tengo un ejemplo como:

x = 5 `div` 0 
test = try (print x) :: IO (Either SomeException()) 

Tengo dos preguntas:

  1. ¿Cómo puedo configurar una salida de error personalizada?

  2. ¿Qué puedo hacer para configurar todos los errores a SomeException así que no debe escribir el :: IO (Either SomeException())

captura/trato

¿Me puede mostrar un ejemplo corto con una salida de error personalizado ?

+2

Re: 3 - lea el [fino manual] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Exception-Base.html): [handle - A version de captura con los argumentos intercambiados; útil en situaciones donde el código para el controlador es más corto] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Exception-Base.html#v:handle). –

Respuesta

110

Cuando Cómo puedo utilizar que funcionan?

Aquí está la recomendación de la documentación Control.Exception:

  • Si usted quiere hacer algo de limpieza en caso de que se produce una excepción, utilizar finally, bracket o onException.
  • Para recuperar después de una excepción y hacer otra cosa, la mejor opción es usar una de la familia try.
  • ... a menos que se esté recuperando de una excepción asincrónica, en cuyo caso use catch o catchJust.

intento :: Exception e => IO a -> IO (O e a)

try toma una acción IO para funcionar, y devuelve un Either. Si el cálculo tuvo éxito, el resultado se da en un constructor Right. (Piensa en lo correcto en lugar de lo incorrecto). Si la acción arrojó una excepción del tipo especificado, se devuelve en un constructor Left. Si la excepción fue no del tipo apropiado, continúa propagándose en la pila. Especificando SomeException como el tipo captará todas las excepciones, lo que puede o no ser una buena idea.

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 del 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 

captura :: Exception e => IO a -> (e -> IO a) -> IO a

catch es similar a try. Primero intenta ejecutar la acción IO especificada, pero si se lanza una excepción, el manejador 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, hay una diferencia importante. Al usar catch, su controlador no puede ser interrumpido por una excepción asincrónica (es decir, lanzada desde otro hilo a través del throwTo). Los intentos de generar una excepción asincrónica se bloquearán hasta que el controlador haya terminado de ejecutarse.

Tenga en cuenta que hay un catch diferente en el Preludio, por lo que es posible que desee hacer import Prelude hiding (catch).

mango :: Exception e => (e -> IO a) -> IO a -> IO a

handle es simplemente catch con los argumentos en el orden inverso. Cuál usar depende de qué hace que su código sea más legible, o cuál se ajusta mejor si desea usar una aplicación parcial. Son por lo demás idénticos.

tryJust, catchJust y handleJust

Tenga en cuenta que try, catch y handle cogerá todos excepciones del tipo especificado/inferido. tryJust y sus amigos le permiten especificar una función de selector que filtra qué excepciones desea específicamente manejar. Por ejemplo, todos los errores aritméticos son del tipo ArithException.Si sólo se desea capturar DivideByZero, que 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 sólo puede ocurrir en el código impuro (es decir, el IO mónada). Si necesita manejar los errores en código puro, debe buscar los valores de retorno usando Maybe o Either (o algún otro tipo de datos algebraicos). Esto a menudo es preferible, ya que es más explícito, por lo que siempre sabrá qué puede pasar. Las mónadas como Control.Monad.Error facilitan el manejo de este tipo de errores.


Consulte también:

+6

bastante informativo, pero me sorprende que haya omitido la regla general de los documentos Control.Exception. Es decir. "use' try', a menos que esté recuperándose de una excepción asincrónica, en cuyo caso use 'catch'" –

+1

@John L: Buen punto. Adicional :) – hammar

1

Re: pregunta 3: catch y handle son same (encontrado a través de hoogle). La elección de cuál usar generalmente dependerá de la duración de cada argumento. Si la acción es más corta, use catch y viceversa. Ejemplo sencillo de mango de la documentación:

do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ... 

También, usted podría ganarse concebible que la función de mango para hacer un controlador personalizado, que luego podría pasar alrededor, por ejemplo. (Adaptado de la documentación): mensajes de error

let handler = handle (\NonTermination -> exitWith (ExitFailure 1)) 

personalizados:

do  
    let result = 5 `div` 0 
    let handler = (\_ -> print "Error") :: IOException -> IO() 
    catch (print result) handler 
1

veo que una cosa que también le (su segunda pregunta) molesta es la escritura de :: IO (Either SomeException()) y me molestó demasiado.

me cambió un poco de código ahora de esto:

let x = 5 `div` 0 
result <- try (print x) :: IO (Either SomeException()) 
case result of 
    Left _ -> putStrLn "Error" 
    Right() -> putStrLn "OK" 

A esto:

let x = 5 `div` 0 
result <- try (print x) 
case result of 
    Left (_ :: SomeException) -> putStrLn "Error" 
    Right() -> putStrLn "OK" 

Para ello, debe utilizar la extensión ScopedTypeVariables GHC pero creo que estéticamente vale la pena.