我试图通过添加对“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错误。有人能告诉我正确的方法来完成我想做的事吗?
答案 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
。