如何使用putStrLn进行跟踪(Haskell)

时间:2011-10-04 15:15:33

标签: haskell monads unsafe-perform-io

我试图通过添加对“putStrLn”的调用来获取Haskell函数以显示它的应用:

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 个答案:

答案 0 :(得分:16)

问题是,Haskell严格区分函数,例如(+)map以及 impure 等操作,例如{{ 1}}和putStrLn。当给定相同的输入而不修改任何东西时,纯函数应该总是产生相同的结果。这显然禁止使用main和朋友。类型系统实际上强制执行这种分离。执行IO或以任何方式不纯的每个函数都有一个PutStr在它的类型前面。


TL;博士;使用模块IO中的trace

Debug.Trace

但要注意结果可能相当令人惊讶,因为无法保证您的代码实际运行;跟踪的参数可以运行一次或两次或任何其他次数。

答案 1 :(得分:3)

每当你遇到类似Couldn't match expected type X with actual type Y这类错误时,都应该使用haskell类型系统来指导你。
那么让我们看看问题是什么:

你有一个类型为Int -> Bool的纯函数。并且你想要打印一些显然不纯的调试输出(即存在于 IO Monad 中)。
但无论如何你要写的是s.th.沿着这些方向:

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

但是如果我们尝试实现它,那就有点不行,因为我们无法逃避IO Monad,因为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

实际上与FUZxxl已经指出的Debug.Trace包中trace函数的结果非常相似。
由于我们同意一个人永远不应该使用 unsafePerformIO,因此首选使用trace函数。请记住,尽管它是纯粹的类型签名,但它实际上也是引用透明并使用下面的unsafePerformIO