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

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

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

4b9b3361

Ответ 1

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

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

Маленький глупый пример. У вас есть эта программа 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, вы увидите, что это все еще неоцененный thunk (обозначенный скобками в GHCi).

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

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

foo :: Int -> Int

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

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

trace :: String -> a -> a

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

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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