Вопрос

Исходя из сообщества Ocaml, я пытаюсь узнать немного Haskell. Переход становится довольно хорошо, но я немного запутался с отладкой. Я использовал (множество) «printf» в моем коде OCAML, чтобы осмотреть некоторые промежуточные значения, либо как флаг, чтобы увидеть, где вычисления точно не удалось.

Поскольку printf - это И. действие, я должен поднять все мой код Haskell внутри И. Монад, чтобы иметь возможность такого рода отладки? Или есть ли лучший способ сделать это (я действительно не хочу делать это вручную, если его можно избежать)

Я также нахожу след Функция:http://www.haskell.org/haskellwiki/debugging#printf_and_friends.что, кажется, именно то, что я хочу, но я не понимаю, что это тип: нет И. в любом месте! Может ли кто-нибудь объяснить мне поведение функции трассировки?

Это было полезно?

Решение

trace проще всего использовать метод отладки. Это не в IO Точно по причине вы указали: не нужно поднимать свой код в IO монад Это реализовано так

trace :: String -> a -> a
trace string expr = unsafePerformIO $ do
    putTraceMsg string
    return expr

Так что за кулисами, но за кулисами, но unsafePerformIO используется, чтобы вырваться из этого. Это функция, которая потенциально сломает референтную прозрачность, которую вы можете угадать, глядя на его тип IO a -> a А также его название.

Другие советы

trace просто сделано нечистым. Точка IO Монад состоит в том, чтобы сохранить чистоту (нет, не незамеченной системой типа) и определить порядок выполнения заявлений, которые в противном случае были бы практически неопределенные с помощью ленивой оценки.

Однако по собственным рискам вы можете взломать вместе IO a -> a, то есть выполнять нечистые IO. Это взлом и конечно «страдает» от ленивых оценок, но это то, что след просто делает ради отладки.

Тем не менее, вы, вероятно, должны пройти другие способы отладки:

  1. Уменьшение потребности в отладке промежуточных значений

    • Напишите небольшие, многоразовые, четкие, общие функции, чья правильность очевидна.
    • Объедините правильные части до более правильных правильных частей.
    • Напишите тесты или попробовать кусочки интерактивно.
  2. Используйте точки останова и т. Д. (Отладка на основе компилятора)

  3. Используйте общие монады. Если ваш код монадичен тем не менее, напишите его независимо от конкретного монады. Использовать type M a = ... вместо простого IO .... Отказ После этого вы можете легко комбинировать монады через трансформаторы и поставить отладочную монаду сверху. Даже если необходимость в монаде ушла, вы могли бы просто вставить Identity a для чистых ценностей.

Для чего это стоит, на самом деле здесь есть два вида «отладки».

  • Регистрация промежуточных значений, таких как значение, которое конкретное подключение имеет на каждого вызова в рекурсивной функции
  • Осмотрите поведение времени выполнения оценки выражения

В строгом обязательном языке они обычно совпадают. В Haskell они часто не:

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

Если вы просто хотите сохранить журнал промежуточных значений, есть много способов сделать это - например, а не поднимать все в IO, простой Writer Монад будет достаточно, это эквивалентное изготовление функций, возвращает 2-кортеж своего фактического результата и значение аккумулятора (как правило, список, как правило,).

Также обычно не нужно ставить все В монаде только функции, которые должны писать в значение «Журнал» - например, вы можете учитывать только под предзависимыми под предсказаниями, которые могут потребоваться выполнить ведение журнала, оставляя основную логику Pure, а затем собрать общий вычислений, сочетая чистые функции и регистрации вычислений в обычном порядке с fmapS и athnot. Имейте в виду, что Writer Видно извините оправдание для монады: без способа читать от Журнал, только напишите ему, каждый вычислений логически не зависит от своего контекста, что облегчает жонглировать вещи.

Но в некоторых случаях даже это излишки - для многих чистых функций, просто движущиеся под воздействием подъемных колоннов и пробуя вещами в REX ROACE.

Если вы хотите на самом деле проверить поведение прохождения чистого кода, однако - например, чтобы выяснить, почему поддержание расходится - в целом есть Нет способа сделать это из другого чистого кода- в самом деле, это по сути определение чистоты. Таким образом, в этом случае у вас нет выбора, кроме как использовать инструменты, которые существуют «снаружи» чистого языка: либо нечистые функции, такие как unsafePerformPrintfDebugging- Грэрр, я имею в виду traceМодифицированная среда выполнения, такая как отладчик GHCI.

trace Также имеет тенденцию переоценивать его аргумент для печати, потерять много преимуществ лени в процессе.

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

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

Если reft + проверьте, полученные значения действительно недостаточно для отладки, упаковывая все в IO, является единственным выбором (но это не правильный способ программирования Haskell).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top