我如何在状态monad中使用Debug.Trace.trace?

som*_*unt 8 haskell

我想跟踪状态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)


Gab*_*lez 5

这是一个替代方案。使用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


ajd*_*ajd 5

当您调用 时execState,您只是要求最终状态,而不是compute函数返回的值。liftM,另一方面,将您的traceThis功能提升到State不触及状态的monad 中的操作。因此,由于懒惰,traceThis只有在您强制评估返回的时才会调用compute。一般来说,为了trace正常工作,您必须确保您调用它的值得到评估。

Debug.Trace通常只适用于快速调试 - 它不是一个非常强大的日志系统,并且由于懒惰而难以使用。如果您正在寻找一种更有效地执行此操作的方法,您可以将另一个元素(可能是一个字符串列表)添加到您的状态元组中,并让compute函数向其写入日志消息。