Vin*_*inz 61 debugging trace haskell printf-debugging
来自Ocaml社区,我正在尝试学习一些Haskell.过渡进展顺利,但我对调试有点困惑.我曾经在我的ocaml代码中放置(很多)"printf",检查一些中间值,或者作为标志来查看计算完全失败的位置.
由于printf是一个IO动作,我是否必须解除IO monad中的所有haskell代码才能进行这种调试?或者有更好的方法来做到这一点(如果可以避免,我真的不想手工做)
我也找到了跟踪功能:http: //www.haskell.org/haskellwiki/Debugging#Printf_and_friends 这看起来正是我想要的,但我不明白它的类型:任何地方都没有IO!有人能解释一下跟踪功能的行为吗?
Dan*_*kov 54
trace
是最容易使用的调试方法.这并不IO
完全是因为你指出的原因:不需要在IO
monad中提升你的代码.它是这样实现的
trace :: String -> a -> a
trace string expr = unsafePerformIO $ do
putTraceMsg string
return expr
Run Code Online (Sandbox Code Playgroud)
所以在幕后有IO但是unsafePerformIO
用来摆脱它.这是一个可能会破坏参照透明度的函数,你可以猜测它的类型IO a -> a
和名称.
Dar*_*rio 17
trace
简直是不纯洁的.IO
monad 的要点是保持纯度(类型系统没有注意到IO)并定义语句的执行顺序,否则通过惰性求值几乎不会定义.
然而,就自己的风险而言,你可以将一些攻击IO a -> a
,即执行不纯的IO.这是一个黑客,当然"受到懒惰评估"的影响,但这就是跟踪只是为了调试而做的事情.
不过,您可能应该采用其他方式进行调试:
减少调试中间值的需要
使用断点等(基于编译器的调试)
使用通用monad.如果你的代码是monadic,那么把它写成独立于具体的monad.使用type M a = ...
而不是普通IO ...
.之后你可以通过变换器轻松组合monad并在它上面放置一个调试monad.即使monad的需求消失了,你也可以插入Identity a
纯值.
C. *_*ann 14
对于它的价值,这里有两种"调试"问题:
在严格的命令式语言中,这些通常是一致的.在Haskell中,他们通常不会:
如果您只想保留中间值的日志,有很多方法可以做到 - 例如,不是将所有内容都解压缩IO
,一个简单的Writer
monad就足够了,这相当于让函数返回它们实际的2元组结果和累加器值(通常是某种列表).
通常也不需要将所有内容放入monad中,只需要写入"log"值的函数 - 例如,您可以只分解可能需要进行日志记录的子表达式,使主逻辑保持纯净,然后通过将常规方式的纯函数和记录计算与fmap
s和whatnot 相结合来重新组装整体计算.请记住,Writer
对于monad来说这是一个令人遗憾的借口:无法从日志中读取,只能写入它,每个计算在逻辑上都与其上下文无关,这使得更容易处理周围的事情.
但在某些情况下,即使是过度杀戮 - 对于许多纯函数来说,只需将子表达式移动到顶层并在REPL中尝试一下就可以了.
但是,如果你想实际检查纯代码的运行时行为 - 例如,要找出子表达式偏离的原因 - 通常没有办法从其他纯代码中做到这一点 - 事实上,这实质上是纯度的定义.因此,在这种情况下,您别无选择,只能使用纯语言"外部"存在的工具:或者是不正确的函数,例如unsafePerformPrintfDebugging
--errr,我的意思是 - trace
或者修改后的运行时环境,例如GHCi调试器.
归档时间: |
|
查看次数: |
9749 次 |
最近记录: |