2011-10-04 32 views
4

我想获得一个Haskell函数来显示被添加调用“putStrLn”时,它被应用于:如何使用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,我得到的错误:

Couldn't match expected type Bool with actual type m0 b0

我敢肯定,这是一个的n00b错误。有人能告诉我正确的方式来完成我想要做的事吗?

+0

你为什么在取平方根之前从'n'中减去1?这只适用于不是质数平方的数字,并且会失败,例如25。 – jwodder

+0

@jwodder,感谢您对素数算法的正确使用。 –

回答

16

的问题是,Haskell有功能,诸如(+)map不纯动作,诸如putStrLnmain严格区别。当给定相同的输入并且不修改任何东西时,纯函数应该总是产生相同的结果。这显然禁止使用PutStr和朋友。类型系统实际上实施了这种分离。每个执行IO或不纯的函数都有一个IO粘在它的类型前面。


tl; dr;使用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))))]))) 

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

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功能,将满足型检查。它将不得不采取一个字符串(你的调试信息)和一个布尔(你的结果),只评估布尔。

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

但是,如果我们尝试实现它,它那种不工作,因为我们无法逃避的IO单子,因为putStrLn的类型是putStrLn :: String -> IO()。为了将其与评估的Bool结合起来,我们将不得不把BoolIO的背景太:

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有我们需要的类型。
所以,现在我们可以实施debugIO方面我们debug功能:

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

这实际上是相当多的,你跟在Debug.Tracetrace功能得到什么作为已经指出的FUZxxl。
而且由于我们同意不应该使用unsafePerformIO,所以使用trace函数是首选。请记住,尽管它是纯粹的类型签名,但它实际上也是而不是参照透明并使用unsafePerformIO下面。

+0

请注意,你应该**从不**使用函数'unsafePerformIO'。几乎总是*更好的解决方案。这个功能是邪恶的;它允许你做一些不可能的事情,并破坏编译器可能做出的一些安全假设。如果你使用'unsafePerformIO',请三思,如果你不是100%肯定的话,*不要使用*!有一些合法用例,比如'trace',但它们很少见。 – fuz

+0

@FUZxxl:我非常了解'unsafePerformIO'的优点和危险。但为了实现espertus所要求的东西,我没有看到其他选择。正如你所指出的那样,“痕迹”实际上是以'unsafePerformIO'来实现的。我会说它属于通常应该用长棒处理的同一类代码。 – oliver

+0

你会不会也会说除了调试之外不应该使用trace?例如,您不应该使用它来打印用户在代码生产时应该阅读的信息? – MatrixFrog