Esto es un apéndice de la respuesta de @barsoap más que cualquier otra cosa.
Las excepciones de Haskell pueden arrojarse a cualquier parte, incluso en código puro, pero solo pueden capturarse dentro de la mónada IO. Para detectar las excepciones arrojadas por el código puro, necesita usar un catch
o try
en la declaración IO que forzaría la evaluación del código puro.
str2Int :: String -> Int -- shortcut so I don't need to add type annotations everywhere
str2Int = read
main = do
print (str2Int "3") -- ok
-- print (str2Int "a") -- raises exception
eVal <- try (print (str2Int "a")) :: IO (Either SomeException())
case eVal of
Left e -> do -- couldn't parse input, try again
Right n -> do -- could parse the number, go ahead
Debe utilizar algo más específico que SomeException
porque eso va a coger cualquier cosa. En el código anterior, el try
devolverá un Left exception
si read
no puede analizar la cadena, pero también devolverá un Left exception
si hay un error de IO al intentar imprimir el valor, o cualquier cantidad de otras cosas que posiblemente podrían salir mal (sin memoria, etc.)
Ahora, aquí está por qué las excepciones del código puro son malas. ¿Qué ocurre si el código IO no obliga realmente a evaluar el resultado?
main2 = do
inputStr <- getLine
let data = [0,1,read inputStr] :: [Int]
eVal <- try (print (head data)) :: IO (Either SomeException())
case eVal of
Right() -> do -- No exception thrown, so the user entered a number ?!
Left e -> do -- got an exception, probably couldn't read user input
Si ejecuta este, encontrará que siempre termina en la rama Right
de la declaración del caso, no importa lo que el usuario ha introducido. Esto se debe a que la acción IO pasada a try
no intenta nunca read
la cadena ingresada. Imprime el primer valor de la lista data
, que es constante y nunca toca el final de la lista. Por lo tanto, en la primera rama de la declaración del caso, el codificador cree que los datos se evalúan pero no es así, y read
aún puede emitir una excepción.
read
está destinado a la deserialización de datos, sin analizar la entrada ingresada por el usuario. Use reads
o cambie a una biblioteca de combinación de analizador real.Me gusta uu-parsinglib, pero parsec, polyparse, y muchos otros también son buenos. Es muy probable que necesite la potencia adicional antes de que pase mucho tiempo.
En el próximo GHC 7.6, habrá 'Text.Read.readEither :: Read a => String -> E String a' y' Text.Read.readMaybe :: Read a => String -> Maybe a'. – sdcvvc