coming from the Ocaml community, I'm trying to learn a bit of Haskell. The transition goes quite well but I'm a bit confused with debugging. I used to put (lots of) "printf" in my ocaml code, to inspect some intermediate values, or as flag to see where the computation exactly failed.
Since printf is an IO action, do I have to lift all my haskell code inside the IO monad to be able to this kind of debugging ? Or is there a better way to do this (I really don't want to do it by hand if it can be avoided)
I also find the trace function : http://www.haskell.org/haskellwiki/Debugging#Printf_and_friends which seems exactly what I want, but I don't understand it's type: there is no IO anywhere! Can someone explain me the behaviour of the trace function ?
Debugging. To debug the main function of a file, press cmd-shift-p (Mac) or ctrl-shift-p (Linux/Windows) to launch the command palette, type in haskell debug and press enter.
Hoed - The Lightweight Haskell Tracer and Debugger Hoed is a tracer/debugger that offers most of HATs functionality, and works with untransformed libraries. Hoed can therefore be used to debug much more programs than traditional tracer/debuggers.
Debug. Trace. Functions for tracing and monitoring execution. These can be useful for investigating bugs or performance problems.
trace
is the easiest to use method for debugging. It's not in IO
exactly for the reason you pointed: no need to lift your code in the IO
monad. It's implemented like this
trace :: String -> a -> a trace string expr = unsafePerformIO $ do putTraceMsg string return expr
So there is IO behind the scenes but unsafePerformIO
is used to escape out of it. That's a function which potentially breaks referential transparency which you can guess looking at its type IO a -> a
and also its name.
trace
is simply made impure. The point of the IO
monad is to preserve purity (no IO unnoticed by the type system) and define the order of execution of statements, which would otherwise be practically undefined through lazy evaluation.
On own risk however, you can nevertheless hack together some IO a -> a
, i.e. perform impure IO. This is a hack and of course "suffers" from lazy evaluation, but that's what trace simply does for the sake of debugging.
Nevertheless though, you should probably go other ways for debugging:
Reducing the need for debugging intermediate values
Use breakpoints etc. (compiler-based debugging)
Use generic monads. If your code is monadic nevertheless, write it independent of a concrete monad. Use type M a = ...
instead of plain IO ...
. You can afterwards easily combine monads through transformers and put a debugging monad on top of it. Even if the need for monads is gone, you could just insert Identity a
for pure values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With