Как достигается отладка в ленивом функциональном языке программирования?

Я хотел бы знать, как отладка достигается на ленивом функциональном языке.
Можете ли вы использовать контрольные точки, печатные выражения и традиционные методы? Это даже хорошая идея?
Насколько я понимаю, чисто функциональное программирование не допускает побочных эффектов, за исключением монад.
Порядок исполнения также не гарантируется.
Придется ли вам программировать монаду для каждого раздела кода, который вы хотите протестировать? Я хотел бы получить представление об этом вопросе от кого-то более опытного в этой области.

Ответы на вопрос(5)

что эта тема может быть решена в короткие сроки. Пожалуйста, прочитайте документы, доступные по следующим ссылкам:

Теория отслеживания чистых функциональных программ.Публикации на Haskell Tracer.Haskell Debugging Technologies.
 franssu26 окт. 2016 г., 10:35
привет, последние две ссылки мертвы!

лем с ленью. Это помогло мне за час устранить утечку памяти из-за лени, которую я уже потратил 2 дня на отладку.

http://www.haskell.org/pipermail/haskell-cafe/2012-January/098847.html

http://hackage.haskell.org/package/htrace

ужасно В Haskell это сложно, но тот факт, что побочные эффекты практически исчезли, устранил большую часть необходимости отладки. Чистые функции чрезвычайно просты для тестирования и проверки без отладчика.

С другой стороны, у меня был опыт пару раз, когда мне нужно было что-то отладить в монаде, и в этом случае я уже смог напечатать / log / что угодно.

По крайней мере, для небольших программ или систем, отладочная разновидность уходит в окно. Строгая типизация и статическая проверка типов действительно еще больше устраняют традиционные ошибки, которые вы обнаружите в процедурном программировании. Большинство ошибок, если они есть, представляют собой логические ошибки (называемые неправильными функциями, математическими ошибками и т. Д.) - их очень легко проверить в интерактивном режиме.

Решение Вопроса

кциональной программе. Разница в стремлении к оценкекогда программа остановится в точке останова и как будет выглядеть трассировка. Программа остановится, когда выражение, на котором установлена точка останова, фактически сокращается (очевидно).

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

Маленький глупый пример. У вас есть эта программа на Haskell.

add_two x = 2 + x

times_two x = 2 * x

foo = times_two (add_two 42)

И вы ставите точку останова на первой строке (add_two), тогда оценитеfoo, Когда программа останавливается на точке останова, на нетерпеливом языке вы ожидаете иметь след

add_two
foo

а такжеtimes_two даже не начал оцениваться, но в отладчике GHCi вы получаете

-1  : foo (debug.hs:5:17-26)
-2  : times_two (debug.hs:3:14-18)
-3  : times_two (debug.hs:3:0-18)
-4  : foo (debug.hs:5:6-27)
<end of history>

это список сокращений, которые привели к сокращению выражения, на которое вы ставите точку останова. Обратите внимание, что это выглядит какtimes_two "называется"foo даже если это не так явно. Из этого видно, что оценка2 * x вtimes_two (-2) форсировали оценку(add_two 42) (-1) изfoo линия. Оттуда вы можете выполнить шаг, как в императивном отладчике (выполнить следующее сокращение).

Еще одно отличие от отладки в нетерпеливом языке заключается в том, что переменные еще не оценены. Например, на шаге -2 в приведенной выше трассировке и проверкеxвы обнаружите, что это все еще неоцененный отрывок (обозначенный скобками в GHCi).

Для более подробной информации и примеров (как пройти через трассировку, проверить значения, ...), см.раздел отладчика GHCi в руководстве GHC. Там такжеЛекса IDE который я еще не использовал, так как я являюсь пользователем VIM и терминалом, но у него есть графический интерфейс отладчика GHCi в соответствии с руководством.

Вы также попросили печатные заявления. Только с чистыми функциями это сделать не так просто, так как оператор print должен находиться внутри монады ввода-вывода. Итак, у вас есть чистая функция

foo :: Int -> Int

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

Это не очень хорошая идея. Итак, вам нужен какой-то способ нарушить чистоту, чтобы получить трассировочные операторы. В Haskell это можно сделать с помощьюunsafePerformIO, Там естьDebug.Trace модуль, который уже имеет функцию

trace :: String -> a -> a

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

Придется ли вам программировать монаду для каждого раздела кода, который вы хотите протестировать?

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

Являются ли методы обязательной отладки хорошей идеей или нет, зависит от ситуации (как обычно). Я считаю тестирование с помощью QuickCheck / SmallCheck гораздо более полезным, чем модульное тестирование на императивных языках, поэтому сначала я бы пошел по этому пути, чтобы избежать как можно больше отладки. Свойства QuickCheck на самом деле дают хорошие лаконичные спецификации функций (для меня много тестового кода на императивных языках выглядит как очередной кусочек кода).

Один из способов избежать отладки - разложить функцию на множество меньших подфункций и протестировать как можно больше из них. Это может быть немного необычно, если исходить из императивного программирования, но это хорошая привычка, независимо от того, какой язык вы используете.

Опять же, отладка! = Тестирование, и если где-то что-то пойдет не так, точки останова и трассировки могут вам помочь.

 Otto Allmendinger24 авг. 2009 г., 23:47
Спасибо за ваш отличный ответ и понимание Хаскелла

Clojure (который ленив, функционален и поощряет, но не обеспечивает чистоту):

Вы можете установить точки останова, как и на любом другом языке. Тем не менее, из-за ленивых вычислений, они могут быть вызваны не сразу, а как только будет произведена оценка, ленивая структура будет запущена.

В ленивых функциональных языках, которые допускают побочные эффекты (включая Clojure), вы можете относительно легко вставлять printlns и другие журналы отладки. Я лично нахожу это очень полезным. Вы должны быть осторожны, когда их вызывают из-за лени, но если вы вообще не видите вывод, это может быть намеком на то, что ваш код не оценивается из-за лени .....

Сказав все вышесказанное, мне никогда не приходилось прибегать к отладчику. Часто нескольких простых тестов (возможно, на REPL) достаточно, чтобы убедиться, что функциональный код работает правильно, и если они терпят неудачу, то обычно совершенно очевидно, что происходит не так.

Ваш ответ на вопрос