Подтвердить что ты не робот

Отслеживание ошибок в Haskell

Как получить дополнительную информацию о том, где произошла ошибка Haskell? Например, вчера я работал над программой Haskell, которая анализирует входной файл, преобразует данные и затем выводит отчетную информацию.

В какой-то момент я побежал "главным" и вернулся

*** Prelude.read: parse error

без какой-либо другой информации. К счастью, я знал, что я звоню, читаю только в одном месте и смог это исправить, но на будущее:

  • Возможно ли получить обратную линию или номер строки для таких ошибок?
  • Возможно ли получить фактические данные, вызвавшие ошибку, т.е. строку, вызвавшую ошибку синтаксического анализа?

Спасибо!

Изменить Использование GHC.

4b9b3361

Ответ 1

вы можете получить строку, которая вызвала ошибку синтаксического анализа, импортировав Debug.Trace и изменив ваш вызов

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)

Ответ 2

Если вы можете запустить код в ghci, отладчик может делать все, что вы хотите. Здесь программа, которая вызывает исключение

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

Теперь загрузите его в ghci и используйте отладчик, как описано здесь: 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> 

Он позволяет вам обходить историю оценки, выделяет фактическое выражение, которое вызывало исключение ( жирный, а не помечено на терминале) и позволяет вам проверять локальные переменные.

Другим вариантом является перекомпиляция с профилированием и некоторыми флагами для маркировки соответствующих МВЗ и запуск с параметром профилирования -xc, который печатает стопку МВЗ на неперехваченных исключениях 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

Причина, по которой это немного сложно, описана чуть раньше на странице отладчика http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci-debugger.html#tracing В принципе, эффективное выполнение Haskell не использует ничего похожего на обычный стек вызовов, поэтому для получения такой информации об исключении, которое вы должны запускать в каком-то специальном режиме (отладка или профилирование), который сохраняет эту информацию.

Ответ 3

В общем, вам решать обработать ошибку таким образом, что вам достаточно контекста для отладки причины.

Лязкость Haskell затрудняет реализацию стеков стека, поскольку стек вызовов может не существовать больше к моменту возникновения ошибки.

Простым способом обработки ошибок является использование типа "Любой", который позволяет возвращать значение, когда все идет правильно, или какой-то контекст (сообщение об ошибке, строка ввода,...) в случае ошибки.

Наконец, в вашем конкретном случае read генерирует исключение, поэтому вам придется поймать его, а затем обработать ошибку в вызывающем коде (посмотрите в пакете Control.Exception).

Ответ 4

Вы не сказали нам, какой компилятор вы используете. Если вы используете GHC, вы должны взглянуть на Отладчик GHCi.

Трассировка стека в Haskell не является тривиальной из-за ее лени. Тем не менее, вышеупомянутый отладчик предоставляет некоторые инструменты (см. Раздел 2.5.5 "Трассировка и история в указанном выше URL-адресе" ).

Ответ 5

Вы можете использовать monadic read, как в "Практический Haskell: сценарий оболочки с обработкой ошибок и разделение привилегий" со стороны StackOverflow пользователь dons:

Первый шаг - заменить read версией, поднятой в общую монаду ошибки, 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]