2012-08-07 93 views
8

我想跟蹤狀態monad中的變化。這不起作用:如何在狀態monad中使用Debug.Trace.trace?

main :: IO() 
main = do 
    print $ snd $ execState compute initialState 

traceThis :: (Show a) => a -> a 
traceThis x = trace ("test: " ++ show x) x 

compute :: State ([Row], Integer) String 
compute = liftM traceThis $ get >>= \(rs, result) -> put (rs, result + 3) >> return "foo" 

沒有被印刷(除了在主要功能的打印已經正確地被更新的最終結果)。

任何意見或方案來跟蹤狀態?我想用這個來檢查項目euler解決方案的正確性。

回答

11

你的情況的問題是traceThis從未得到評估。 Haskell是一種懶惰的語言,因此它只評估所需的表達式。而且由於您不評估計算結果,只有狀態,因此不需要在compute內部評估traceThis。如果打印例如

print $ evalState compute initialState 

則狀態計算的結果值獲取與調用traceThis評估一起。

一個更好的選擇將是確定強制打印結果值只要一元計算的任何部分進行評估一個一元函數:

traceState :: (Show a) => a -> State s a 
traceState x = state (\s -> trace ("test: " ++ show x) (x, s)) 

compute :: State ([Int], Integer) String 
compute = get >>= \(rs, result) -> put (rs, result + 3) 
       >> return "foo" 
       >>= traceState 

更新:這可以推廣到任意的單子。主要的一點是,trace必須包裹一元計算,不只是裏面的值,以便它能夠評估時>>=評估,不管裏面的值是否被評估或不:

traceMonad :: (Show a, Monad m) => a -> m a 
traceMonad x = trace ("test: " ++ show x) (return x) 
+0

非常好!一旦我不再需要記錄,我可以通過刪除單個表達式來關閉它。 – somesoaccount 2012-08-07 13:32:46

+0

@somesoaccount用更一般的解決方案更新了答案。 – 2014-08-30 07:51:13

4

這裏是一個另類。使用StateT s IO爲您的單子:

compute :: StateT ([Row], Integer) IO String 
compute = do 
    (rs, result) <- get 
    lift $ putStrLn "result = " ++ show result 
    put (rs, result + 3) 
    return "foo" 

現在你可以使用交錯隨時隨地lift行動IO

要了解更多關於單子變壓器,我建議你讀了精彩的介紹:Monad Transformers - Step by Step

+0

這似乎是一個簡單的解決方案,我唯一的罪魁禍首是使用monad變壓器,因爲我聽說他們很慢,我需要在這裏表現不錯。 – somesoaccount 2012-08-07 13:31:07

+1

無論您是否意識到這一點,您已經在爲monad變壓器付費。「狀態」只是「狀態標識」的同義詞。 [證明](http://hackage.haskell.org/packages/archive/transformers/0.3.0.0/doc/html/Control-Monad-Trans-State-Lazy.html#t:State)。另外,大多數關於單聲道變壓器性能的擔憂都被誇大了。我建議你自己對它進行基準測試,並在解僱之前與「Debug.Trace」的性能進行比較。 – 2012-08-07 17:22:36

4

當您致電execState時,您只是要求最終狀態,而不是compute函數返回的值。另一方面,liftM會將您的traceThis函數提升到State單元中未觸及狀態的操作。因此,由於懶惰,traceThis只會如果強制通過compute返回進行評估調用。一般來說,爲使trace正常工作,您必須確保您所調用的值得到評估。

Debug.Trace一般只適用於快速調試 - 它不是一個非常強大的日誌系統,可以是困難的,因爲使用懶惰。如果你正在尋找一種更強大的方法來實現這一點,你可以在你的狀態元組中添加另一個元素(可能是一個字符串列表),並且把日誌消息寫入到這個元組中。

+0

感謝您的解釋。我想到懶惰,但由於打印結果我放棄了這種可能性... 我不確定是否我喜歡添加廣泛的日誌記錄,因爲它將在出於性能原因將被刪除。 – somesoaccount 2012-08-07 12:59:22