Всякий раз, когда у вас возникают такие ошибки типа, как 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
снизу.