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