2010-01-26 25 views
14

Jak uzyskać więcej informacji o tym, gdzie wystąpił błąd Haskell? Na przykład wczoraj pracowałem nad programem Haskell, który analizuje plik wejściowy, przekształca dane, a następnie drukuje informacje raportowania.Śledzenie błędów w Haskell

W pewnym momencie wpadłem "main" i wróciłem

*** Prelude.read: parse error 

bez innych informacji. Na szczęście wiedziałem, że dzwonię do odczytu tylko w jednym miejscu i udało mi się to naprawić, ale na przyszłość:

  • Czy można uzyskać ślad wstecz lub numer linii dla takich błędów?
  • Czy można uzyskać rzeczywiste dane, które spowodowały błąd, tj. Ciąg, który spowodował błąd analizy?

Dzięki!

Edytuj Używanie GHC.

+0

Lepiej jest po prostu całkowicie unikać częściowych funkcji. Zamiast tego użyj "Safe.readMay". –

+0

zobacz [tutaj] (http://stackoverflow.com/questions/8595077/how-can-i-get-position-where-error-was-called) dla lepszego rozwiązania – Simon

Odpowiedz

5

możesz uzyskać ciąg, który spowodował błąd analizy, importując Debug.Trace i zmieniając ty r zadzwoń

 
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

Znajduję '\ s-> fst $ head $ czyta s ++ [błąd $ "Nie można odczytać:" ++ show s] "jest bardziej użyteczny, ponieważ nie powoduje spamu, gdy czytanie się powiedzie. – Rotsor

2

Nie podano nam, z którego kompilatora korzystasz. Jeśli używasz GHC, powinieneś rzucić okiem na GHCi Debugger.

Śledzenie stosu w Haskell nie jest trywialne ze względu na lenistwo. Mimo to wyżej wymieniony debugger udostępnia kilka narzędzi (patrz sekcja 2.5.5 Śledzenie i historia w powyższym adresie URL).

3

Generalnie od ciebie zależy, jak poradzić sobie z błędem, aby było wystarczająco dużo kontekstu do debugowania przyczyny.

Leniwość Haskella utrudnia śledzenie stosu, ponieważ stos wywoławczy może nie istnieć już w momencie wystąpienia błędu.

Prostym sposobem obsługi błędów jest użycie dowolnego typu, który pozwala na zwrócenie wartości, gdy wszystko poszło dobrze, lub kontekstu (komunikat o błędzie, ciąg wejściowy, ...) w przypadku błędu.

Wreszcie, w twoim konkretnym przypadku read rzuca wyjątek, więc musisz go złapać, a następnie obsłużyć błąd w kodzie wywołującym (zajrzyj do pakietu Control.Exception).

1

Można rozważyć użycie jednowartościowy read jak w "Practical Haskell: shell scripting with error handling and privilege separation" przez innych StackOverflow użytkownik dons:

Pierwszym krokiem jest zastąpienie read z wersją podnoszone do ogólnego błędu monady, 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

Jeśli możesz uruchomić kod w ghci, to debugger może zrobić wszystko, co chcesz.Oto program, który podnosi wyjątek

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

Teraz załadować je do ghci i używać debuggera, co zostało udokumentowane tutaj: 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> 

Pozwala obejść w historii oceny, podkreśla rzeczywisty wyrażenie, które podniosło wyjątek (pogrubiony zamiast oznaczonego gwiazdką na terminalu) i pozwala sprawdzić lokalne zmienne.

Inną opcją jest przekompilować z profilowaniem i niektóre flagi, aby oznaczyć odpowiednie MPK i uruchomić z -xc opcji profilowania która drukuje środkowy stos na koszt Uncaught wyjątkami 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 

Powodem tego jest nieco trudniej jest opisana nieco wcześniej na stronie debuggera: Zasadniczo, wydajna realizacja Haskella nie używa niczego przypominającego zwykły stos wywołań, więc aby uzyskać ten rodzaj informacji na temat wyjątku, musisz działać w jakimś specjalnym trybie (debugowanie lub profilowanie), które to zachowuje rodzaj informacji.