Как использовать putStrLn для трассировки (Haskell) - PullRequest
4 голосов
/ 04 октября 2011

Я пытаюсь заставить функцию Haskell отображаться всякий раз, когда она применяется, добавляя вызов "putStrLn":

isPrime2 1 = False

isPrime2 n = do
    putStrLn n
    null (filter (==0) (map (mod n) (filter isPrime2 [2..(floor (sqrt(fromIntegral (n-1))))])))

(Конечная цель - показать, почему одна версия isPrime более эффективначем другое.)

Когда я загружаю вышеуказанный код в GHCi, я получаю ошибку:

Не удалось сопоставить ожидаемый тип Bool с фактическим типом m0 b0

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

Ответы [ 2 ]

16 голосов
/ 04 октября 2011

Проблема в том, что Haskell строго различает чистые функции, такие как (+) и map и нечистые действия, такие как putStrLn и main. Предполагается, что чистая функция всегда дает один и тот же результат, если дан один и тот же ввод и ничего не изменяется. Это явно запрещает использование PutStr и друзей. Система типов фактически обеспечивает это разделение. Каждая функция, которая выполняет IO или является нечистой в любом случае, имеет знак IO перед своим типом.


ТЛ; др; используйте trace из модуля Debug.Trace:

import Debug.Trace

isPrime2 1 = False
isPrime2 n = show n `trace` null (filter (==0) (map (mod n) (filter isPrime2 [2..(floor (sqrt(fromIntegral (n-1))))])))

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

4 голосов
/ 04 октября 2011

Всякий раз, когда у вас возникают такие ошибки типа, как Couldn't match expected type X with actual type Y, вы должны использовать систему типов haskell, чтобы вести вас.
Итак, давайте посмотрим, в чем проблема:

У вас есть чистая функция с типом Int -> Bool. И вы хотите напечатать какой-нибудь отладочный вывод, который явно не чистый (т.е. который находится в IO Monad ).
Но в любом случае, то, что вы хотите написать, вдоль этих линий:

foo x 
  | x > 0 = debug "ok" True
  | otherwise = debug "ohhh...no" False

Тем не менее, тип вашей функции должен быть foo :: Int -> Bool

Итак, давайте определим функцию debug, которая будет удовлетворять проверке типов. Это должно было бы взять String (ваше сообщение отладки) и Bool (ваш результат) и оценивать только как Bool.

debug :: String -> Bool -> Bool
debug = undefined

Но если мы попытаемся реализовать его, это не сработает, поскольку мы не можем избежать монады ввода-вывода, поскольку тип putStrLn равен putStrLn :: String -> IO (). Чтобы объединить его с оценкой Bool, нам нужно будет поставить Bool в контексте IO:

debugIO msg result = putStrLn msg >> return result

Хорошо, давайте спросим ghci для типа этой функции:

Main> :t debugIO
debugIO :: String -> b -> IO b

Итак, мы получаем IO Bool, но нам понадобится Bool.
Есть ли функция с типом IO b -> b? Быстрый поиск по hoogle дает нам подсказку:

Печально известный unsafePerformIO :: IO a -> a имеет тип, который нам нужен здесь.
Так что теперь мы можем реализовать нашу debug функцию в терминах debugIO:

debug :: String -> Bool -> Bool
debug s r = unsafePerformIO $ debugIO s r

, что на самом деле очень похоже на то, что вы получаете с помощью функции trace в пакете Debug.Trace, как уже указывалось FUZxxl.
И поскольку мы согласны с тем, что никогда не следует использовать unsafePerformIO, использование функции trace является предпочтительным. Просто имейте в виду, что, несмотря на то, что это чисто сигнатура типа, она на самом деле также не ссылочно-прозрачная и использует unsafePerformIO снизу.

...