5

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

4

2 回答 2

18

问题是,Haskell 对诸如and之类的函数和诸如and之类的(+)不纯函数有严格的区分。当给定相同的输入并且不修改任何内容时,纯函数应该始终产生相同的结果。这显然禁止使用和朋友。类型系统实际上强制执行这种分离。每个执行 IO 或以任何方式不纯的函数在其类型前面都有一个标记。mapputStrLnmainPutStrIO


tl;博士; 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))))])))

但请注意,结果可能相当令人惊讶,因为无法保证您的代码会真正运行;trace 的参数可以运行一次或两次或任何其他次数。

于 2011-10-04T15:25:01.140 回答
4

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

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

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

不过,您的功能类型应该是foo :: Int -> Bool

因此,让我们定义一个debug满足类型检查器的函数。它必须接受一个字符串(你的调试消息)和一个布尔值(你的结果),并且只计算布尔值。

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

但是如果我们尝试实现它,它就会不起作用,因为我们无法逃脱 IO Monad,因为 putStrLn 的类型是putStrLn :: String -> IO (). 为了将它与对 a 的评估结合起来,Bool我们必须将 theBool置于 the 的上下文中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 已经指出的那样,这实际上几乎是您使用包中的trace函数所获得的。 并且由于我们同意永远不要使用该功能的用法。请记住,尽管它是纯类型签名,但它实际上也不是引用透明的并且在下面使用。Debug.Trace
unsafePerformIOtraceunsafePerformIO

于 2011-10-04T17:27:05.290 回答