我想跟踪状态monad的变化.这不起作用:
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"
Run Code Online (Sandbox Code Playgroud)
没有打印任何内容(除了主函数中已正确更新的打印的最终结果).
跟踪状态的任何想法或替代方案?我想用它来检查项目euler解决方案的正确性.
Pet*_*lák 11
您的案例中的问题traceThis
是永远不会得到评估.Haskell是一种惰性语言,因此它只评估所需的表达式.既然你不评估计算结果,只评估状态,就没有必要在traceThis
里面进行评估compute
.如果你打印例如
print $ evalState compute initialState
Run Code Online (Sandbox Code Playgroud)
然后,有状态计算的结果值与调用一起被评估traceThis
.
更好的选择是定义一个monadic函数,在评估monadic计算的任何部分时强制打印结果值:
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
Run Code Online (Sandbox Code Playgroud)
更新:这可以推广到任意monad.重点是trace
必须包装monadic计算,而不仅仅是内部的值,以便在评估时>>=
对其进行评估,无论是否评估内部值:
traceMonad :: (Show a, Monad m) => a -> m a
traceMonad x = trace ("test: " ++ show x) (return x)
Run Code Online (Sandbox Code Playgroud)
这是一个替代方案。使用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"
Run Code Online (Sandbox Code Playgroud)
现在,您可以IO
在任何地方使用lift
.
要了解有关 monad 转换器的更多信息,我建议您阅读出色的介绍:Monad Transformers - Step by Step。
当您调用 时execState
,您只是要求最终状态,而不是compute
函数返回的值。liftM
,另一方面,将您的traceThis
功能提升到State
不触及状态的monad 中的操作。因此,由于懒惰,traceThis
只有在您强制评估返回的值时才会调用compute
。一般来说,为了trace
正常工作,您必须确保您调用它的值得到评估。
Debug.Trace
通常只适用于快速调试 - 它不是一个非常强大的日志系统,并且由于懒惰而难以使用。如果您正在寻找一种更有效地执行此操作的方法,您可以将另一个元素(可能是一个字符串列表)添加到您的状态元组中,并让compute
函数向其写入日志消息。
归档时间: |
|
查看次数: |
1723 次 |
最近记录: |