Отладка кода на Haskell с помощью Debug.Trace
Что ни говори, а отладочный вывод был и остается одним из самых простых и часто используемых способов отладки. Но как прикажите использовать его в Haskell при написании чистых функций? Временно оборачивать функцию в монаду IO, а по завершении отладки возвращать код к прежнему состоянию? Разумеется, нет!
Есть такой «читерный» модуль под названием Debug.Trace. Он идет вместе с GHC. В этом модуле объявлено несколько функций для отладочного вывода. Они кажутся чистыми, но на самом деле, по понятным причинам, такими не являются.
Рассмотрим эти функции.
trace :: String -> a -> a
Функция trace выводит строку, переданную первым аргументом, и возвращает то, что было передано вторым аргументом. Как видите, эта функция притворяется чистой, но в действительности у нее есть побочный эффект.
traceShow :: Show a => a -> b -> b
Функция traceShow аналогична trace, только первый аргумент преобразуется в строку с помощью функции show. Чтобы вывести с помощью traceShow несколько значений, можно, например, объединить их в кортеж. Как и trace, функция traceShow возвращает свой второй аргумент.
traceStack :: String -> a -> a
Функция traceStack аналогична функции trace, но помимо своего первого аргумента она также выводит стек вызовов, если он доступен. Чтобы traceStack выводила стек вызовов, программа должна быть собрана с флагом -prof (если вы используете GHC).
traceIO :: String -> IO ()
Функция traceIO делает практически то же самое, что putStrLn. Пожалуй, единственное польза от нее заключается в том, что эту функцию легко найти по имени и затем избавиться от ее вызова.
Пример использования Debug.Trace:
$ ghci
> import Debug.Trace
> let func x = trace ( "x = " ++ show x) $ x + 1
> func 1
x = 1
2
Итак, если раньше для поиска ошибок мы имели статическую типизацию, REPL, логи, если они ведутся, и модульные тесты, если вы их пишите, то теперь в нашем арсенале появилось и традиционное распихивание варнов по всему коду. Само собой разумеется, что использовать Debug.Trace следует только во время отладки!
А как вы отлаживаете свой код на Haskell?