Haskell 如何在状态monad中使用Debug.Trace.Trace?
我想跟踪单子状态的变化。这不起作用:Haskell 如何在状态monad中使用Debug.Trace.Trace?,haskell,Haskell,我想跟踪单子状态的变化。这不起作用: 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 &g
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"
不会打印任何内容(除了主函数中已正确更新的打印的最终结果)
对追踪状态有什么想法或替代方案吗?我想用它来检查project euler解决方案的正确性。这里有一个替代方案。使用
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"
compute::StateT([Row],Integer)IO字符串
计算=do
(rs,result)当您调用execState
时,您只是请求最终状态,而不是请求compute
函数返回的值<另一方面,code>liftM
会将trace此
函数提升到状态下的动作
单子,该单子不接触该状态。因此,由于惰性,trace只有在您强制计算compute
返回的值时才会调用此
。通常,要使trace
正常工作,必须确保对调用它的值进行求值
Debug.Trace
通常只适用于快速调试-它不是一个功能非常强大的日志系统,并且可能由于懒惰而难以使用。如果您正在寻找一种更有效的方法,可以向状态元组中添加另一个元素(可能是字符串列表),并让compute
函数写入日志消息。
您案例中的问题是,traceThis
从未得到评估。Haskell是一种惰性语言,因此它只计算需要的表达式。由于您不计算计算结果,只计算状态,因此不必在compute
中计算traceThis
。例如,如果您打印
打印$evalState计算初始状态
然后在调用traceThis
的同时计算有状态计算的结果值
更好的选择是定义一元函数,该函数在计算一元计算的任何部分时强制打印结果值:
tracstate::(显示a)=>a->状态s a
traceState x=状态(\s->trace(“测试:++显示x)(x,s))
compute::State([Int],整数)字符串
compute=get>>=\(rs,result)->put(rs,result+3)
>>返回“foo”
>>=地产
更新:这可以推广到任意单子。要点是,trace
必须包装一元计算,而不仅仅是内部的值,以便在计算>=
时对其进行计算,而不管内部的值是否计算:
traceMonad::(显示a,单子m)=>a->m a
traceMonad x=跟踪(“测试:”++显示x)(返回x)
谢谢您的解释。我也想到了懒惰,但由于结果被打印出来,我放弃了这种可能性。。。我不确定我是否喜欢添加大量日志记录,因为出于性能原因,它将在将来被删除。这似乎是一个简单的解决方案,我唯一的罪魁祸首是使用monad transformer,因为我听说它们很慢,我在这里需要良好的性能。无论您是否意识到,您已经为monad transformer支付了费用<代码>状态s只是状态s标识的类型同义词。此外,大多数关于monad变压器性能的担忧被夸大了。我建议您自己对其进行基准测试,并与Debug.Trace
的性能进行比较,然后再关闭它。非常好!一旦我不再需要日志记录,我可以通过删除一个表达式来关闭它。@somesoaccount用一个更通用的解决方案更新了答案。