2011-10-04 37 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