我想跟踪state 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"不会打印任何内容(除了main函数中打印的最终结果,该结果已正确更新)。
有什么想法或替代方案来跟踪状态吗?我想用它来检查项目euler解决方案的正确性。
发布于 2012-08-07 20:49:23
在您的案例中,问题是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更新:这可以推广到任意的monad。要点是,trace必须包装一元计算,而不仅仅是内部的值,以便在计算>>=时对其进行计算,而不管内部的值是否进行计算:
traceMonad :: (Show a, Monad m) => a -> m a
traceMonad x = trace ("test: " ++ show x) (return x)发布于 2012-08-07 20:40:45
这里有一个替代方案。使用StateT s IO作为您的monad:
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的更多信息,我建议您阅读优秀的介绍:Monad Transformers - Step by Step。
发布于 2012-08-07 20:44:57
当您调用execState时,您只是请求最终的状态,而不是compute函数返回的值。另一方面,liftM将您的traceThis函数提升为State monad中的一个不涉及状态的操作。因此,由于惰性,只有在强制计算compute返回的值的情况下才会调用traceThis。通常,为了让trace正常工作,您必须确保调用它的值得到计算。
Debug.Trace通常只适用于快速调试-它不是一个功能非常强大的日志系统,并且由于懒惰而很难使用。如果您正在寻找一种更健壮的方法,您可以向状态元组添加另一个元素(可能是一个字符串列表),并让compute函数向其中写入日志消息。
https://stackoverflow.com/questions/11845863
复制相似问题