2010-01-26 12 views
14

Come posso ottenere maggiori informazioni su dove si è verificato un errore Haskell? Ad esempio, ieri stavo lavorando a un programma Haskell che analizza un file di input, trasforma i dati e poi stampa le informazioni di reporting.Errori di tracciamento in Haskell

A un certo punto, mi sono imbattuto "principale" e tornati

*** Prelude.read: parse error 

senza altre informazioni. Fortunatamente, sapevo che stavo chiamando in un solo posto ed ero in grado di risolverlo, ma per il futuro:

  • E 'possibile ottenere un backtrace o un numero di riga per errori come questi?
  • È possibile ottenere i dati effettivi che hanno attivato l'errore, ovvero la stringa che ha causato l'errore di analisi?

Grazie!

Modifica Utilizzo di GHC.

+0

Stai meglio evitando completamente le funzioni parziali. Usa invece 'Safe.readMay'. –

+0

vedere [qui] (http://stackoverflow.com/questions/8595077/how-can-i-get-the-position-where-error-was-called) per una soluzione migliore – Simon

risposta

5

puoi ottenere la stringa che ha causato l'errore di analisi importando Debug.Trace e modificando tu r chiamare

 
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

trovo '\ s-> fst $ head $ read s ++ [error $ "Impossibile leggere:" ++ show s] 'più utile perché non fa spam quando la lettura ha successo. – Rotsor

2

Non ci hai detto quale compilatore stai usando. Se usi GHC, dovresti dare un'occhiata allo GHCi Debugger.

Il tracciamento dello stack in Haskell non è banale, a causa della sua pigrizia. Tuttavia, il debugger di cui sopra fornisce alcuni strumenti (vedere la sezione 2.5.5. Tracciamento e cronologia nell'URL precedente).

3

In generale, è compito dell'utente gestire l'errore in modo tale da disporre di un contesto sufficiente per il debug della causa.

La pigrizia di Haskell rende difficili le implementazioni dello stack, poiché lo stack di chiamate potrebbe non esistere più nel momento in cui si verifica l'errore.

Un modo semplice di gestione degli errori consiste nell'utilizzare il tipo Either che consente di restituire un valore quando le cose sono andate a destra, o in un contesto (messaggio di errore, stringa di input, ...) in caso di errore.

Infine, nel tuo caso specifico read sta generando un'eccezione in modo da doverla rilevare e quindi gestire l'errore nel codice chiamante (dare un'occhiata al pacchetto Control.Exception).

1

Si potrebbe considerare l'utilizzo di una monade read come in "Practical Haskell: shell scripting with error handling and privilege separation" dal collega StackOverflow utente dons:

Il primo passo è quello di sostituire read con una versione sollevato in una monade errore generico, 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

Se è possibile eseguire il codice in ghci, allora il debugger può fare tutto quello che volete.Ecco un programma che solleva un'eccezione

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

Ora caricarlo in ghci e utilizzare il debugger, come documentato qui: 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> 

Esso consente di passo intorno alla storia di valutazione, mette in evidenza l'attuale espressione che ha sollevato l'eccezione (in grassetto anziché contrassegnata da un terminale) e consente di controllare le variabili locali.

Un'altra opzione è quella di ricompilare con profili e alcune bandiere contrassegnare centri di costo appropriate, e correre con l'opzione di profilazione -xc che stampa la pila centro di costo sulle eccezioni non gestite fanno 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 

Il motivo di questo è un po 'difficile è descritto un po' prima nella pagina del debugger http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci-debugger.html#tracing In pratica, un'esecuzione efficiente di Haskell non utilizza nulla che assomigli ad un normale stack di chiamate, quindi per ottenere quel tipo di informazione su un'eccezione devi essere in esecuzione in qualche modo speciale (debugging o profiling) che lo mantiene una specie di informazione