2010-01-26 24 views
14

¿Cómo puedo obtener más información sobre dónde ha ocurrido un error Haskell? Por ejemplo, ayer estaba trabajando en un programa Haskell que analiza un archivo de entrada, transforma los datos y luego imprime la información de informes.Seguimiento de errores en Haskell

En un momento dado, me encontré con "principal" y volvió

*** Prelude.read: parse error 

con ninguna otra información. Afortunadamente, sabía que estaba llamando leído en un solo lugar y fue capaz de arreglarlo, pero para el futuro:

  • ¿Es posible conseguir una traza o un número de línea de errores como estos?
  • ¿Es posible obtener los datos reales que desencadenaron el error, es decir, la cadena que provocó el error de análisis?

Gracias!

Editar Usando GHC.

+0

Será mejor que evite funciones parciales por completo. Use 'Safe.readMay' en su lugar. –

+0

ver [aquí] (http://stackoverflow.com/questions/8595077/how-can-i-get-the-position-where-error-was-llamado) para una mejor solución – Simon

Respuesta

5

puede obtener la cadena que causó el error de análisis importando Debug.Trace y cambiando r llamar

 
import Debug.Trace (trace) 

--change 
myRead s = read s 
--to 
myRead s = trace s (read s) 
--or 
myRead s = trace (take 100 s) (read s) 
+0

Me parece '\ s-> fst $ head $ lee s ++ [error $ "No se puede leer:" ++ show s] "más útil porque no envía correo no deseado cuando la lectura es correcta. – Rotsor

2

No nos dijo qué compilador está utilizando. Si usa GHC, entonces debería echar un vistazo al GHCi Debugger.

El seguimiento de la pila en Haskell no es trivial, debido a su pereza. Sin embargo, el depurador antes mencionado proporciona algunas herramientas (consulte la sección 2.5.5. Rastreo e historial en la URL anterior).

3

En general, le corresponde a usted manejar el error de tal manera que haya contexto suficiente para que pueda depurar la causa.

La laxitud de Haskell hace que los rastros de pila sean difíciles de implementar, porque la pila de llamadas puede que ya no exista en el momento en que ocurre el error.

Una forma simple de manejo de errores es usar el tipo que le permite devolver un valor cuando las cosas salieron bien, o algún contexto (mensaje de error, cadena de entrada, ...) en caso de error.

Finalmente, en su caso específico read está lanzando una excepción, por lo que tendría que detectar eso y luego manejar el error en el código de llamada (eche un vistazo al paquete Control.Exception).

1

Se podría considerar el uso de un monádico read como en "Practical Haskell: shell scripting with error handling and privilege separation" por su compañero Stackoverflow usuario dons:

El primer paso es reemplazar read con una versión levantó en una mónada error genérico, MonadError:

readM :: (MonadError String m, Read a) => String -> m a 
readM s | [x] <- parse = return x 
     | otherwise = throwError $ "Failed parse: " ++ show s 
    where 
     parse = [x | (x,t) <- reads s] 
7

Si puede ejecutar el código en ghci, entonces el depurador puede hacer todo lo que desee.He aquí un programa que genera una excepción

foo s i 
    | i == 57 = read s 
    | otherwise = i 
main = mapM_ (print . foo "") [1..100] 

Ahora cargarlo en ghci y utilizar el depurador, tal como se documenta aquí: http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci-debugger.html#ghci-debugger-exceptions

> ghci test.hs 
*Main> :set -fbreak-on-error 
*Main> :trace main 
1 
2 
... snipped 3 through 55 ... 
56 
Stopped at <exception thrown> 
_exception :: e = _ 
[<exception thrown>] *Main> :back 
Logged breakpoint at test.hs:2:15-20 
_result :: a 
s :: String 
[-1: test.hs:2:15-20] *Main> :list 
1 foo s i 
2 | i == 57 = **read s** 
3 | otherwise = i 
[-1: test.hs:2:15-20] *Main> s 
"" 
[-1: test.hs:2:15-20] *Main> 

Se deja paso alrededor de la historia de evaluación, destaca la real expresión que generó la excepción (negrita en lugar de aparecer en un terminal) y le permite inspeccionar las variables locales.

Otra opción es volver a compilar con perfiles y algunas banderas etiquetar los centros de costos adecuados, y ejecutar con la opción de perfiles -xc que imprime la pila de centro de costos sobre las excepciones no capturadas http://www.haskell.org/ghc/docs/latest/html/users_guide/prof-time-options.html

> ghc -prof -auto-all test.hs 
> ./test +RTS -cs 
1 
2 
... snipped 3 through 55 ... 
56 
*** Exception (reporting due to +RTS -xc): (THUNK_2_0), stack trace: 
    Main.foo, 
    called from Main.main, 
    called from Main.CAF 
    --> evaluated by: Main.main, 
    called from Main.CAF 
test: Prelude.read: no parse 

La razón de esto es un poco difícil se describe un poco antes en la página del depurador Básicamente, la ejecución eficiente de Haskell no utiliza nada parecido a una pila de llamadas normal, por lo que para obtener ese tipo de información en una excepción debe ejecutarse en algún modo especial (depuración o creación de perfiles) que mantiene eso tipo de información.

Cuestiones relacionadas