Hi all, does anyone have any tips on how to insert debugging or logging statements through a program? Here are two possibilities: 1. Use some kind of logging monad. But then all your types become more complicated and it seems you have to use lots of ->'s and >>='s instead of ordinary function application. It seems this can make the code much less elegant. Also it seems that every time you want to add logging to a function, you have to change its type, and then change the type of all the functions that call it, etc. Should it really be this complicated? 2. Use unsafePerformIO or similar. But then your code is regularly defeating the type system, which presumably isn't good. Also the ghc manual has special instructions for compiling code that uses unsafePerformIO, intimating that it isn't used very often. It seems this must come up a lot when writing Haskell programs. What do most pople do? -- Ben Escoto
Ben Escoto <bescoto@stanford.edu> writes:
Hi all, does anyone have any tips on how to insert debugging or logging statements through a program?
1. Use some kind of logging monad.
2. Use unsafePerformIO or similar.
It seems this must come up a lot when writing Haskell programs. What do most pople do?
The latter. In my case, it's more for debugging than logging, so I can live with somewhat weak guarantees. If you need to do it more properly, the simple solution might be to limit logging to when you're in the IO monad anyway? -kzm -- If I haven't seen further, it is by standing in the footprints of giants
Ben Escoto writes:
1. Use some kind of logging monad.
This is definitely the way to go. The 'MonadWriter' class provides a very general interface, which often proves to be much more useful than just for logging. Especially, when you use it to return data types with useful information -- rather than just strings. And 'mapWriter' gives you much flexibility, when it comes to creating glue code to convert different types of log events. You can use the transforming variant to encapsulate other monads, like IO a. Then, running code that is unaware of logging requires a call to 'lift', and that's it. I really don't see the need to resort to unsafePerformIO in this case. Which, besides, has the disadvantage of being hard to selectively enable/disable, it's hard to write to anywhere but stderr, and it's, well, unsafe. :-) Peter
On 16 Nov 2003 22:43:55 +0100 Peter Simons <simons@cryp.to> wrote:
This is definitely the way to go. The 'MonadWriter' class provides a very general interface, which often proves to be much more useful than just for logging. Especially, when you use it to return data types with useful information -- rather than just strings. And 'mapWriter' gives you much flexibility, when it comes to creating glue code to convert different types of log events.
You can use the transforming variant to encapsulate other monads, like IO a. Then, running code that is unaware of logging requires a call to 'lift', and that's it.
Thanks for the pointer. I had seen Control.Monad.Writer in the ghc docs but previously didn't know what is was good for. I will try to learn how to use the various monad transformers and see how easy they are elegant to use in practice. For debugging (as opposed to logging) Debug.Trace looks good, thanks to the other poster(s) for mentioning that. Maybe eventually I will see a need for mapWriter. As a passing thought, I wonder how many programmers can read the mapWriterT documentation: mapWriterT :: (m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b and start pounding the code out? Anyway, once I get this all sorted out, I figure my program will have really nice logging, better than fprintf(stderr, ...) even. -- Ben Escoto
hello, Ben Escoto wrote:
Maybe eventually I will see a need for mapWriter. As a passing thought, I wonder how many programmers can read the mapWriterT documentation:
mapWriterT :: (m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b
and start pounding the code out? Anyway, once I get this all sorted out, I figure my program will have really nice logging, better than fprintf(stderr, ...) even.
you should not use mapWriterT, usually you can get the same behaviour in a different way (i think it should not be exported by the module). by the way once you learn how the tranformers work, you'll be able to guess what that does from its type. however i think it is nice if you can use the transformers witout knowing how they are implemented. if you have access to cvs you might want to look at the revamped monad transformer library in the haskell cvs (fptools/libraries/monads). it is more docuemnted then the current one, but otherwise it is very similar. bye iavor -- ================================================== | Iavor S. Diatchki, Ph.D. student | | Department of Computer Science and Engineering | | School of OGI at OHSU | | http://www.cse.ogi.edu/~diatchki | ==================================================
Ben Escoto <bescoto@stanford.edu> wrote,
Hi all, does anyone have any tips on how to insert debugging or logging statements through a program? Here are two possibilities: [..] 2. Use unsafePerformIO or similar. But then your code is regularly defeating the type system, which presumably isn't good. Also the ghc manual has special instructions for compiling code that uses unsafePerformIO, intimating that it isn't used very often.
There is `Debug.Trace.trace' for this: http://haskell.org/ghc/docs/latest/html/base/Debug.Trace.html However, if you want to log as opposed to debug, you may want to have the output go to somewhere else but stdout. Hence, it might be useful to have a variant of the `trace' that takes an explicit file handle in the library. Cheers, Manuel
Manuel M T Chakravarty wrote:
There is `Debug.Trace.trace' for this:
http://haskell.org/ghc/docs/latest/html/base/Debug.Trace.html
However, if you want to log as opposed to debug, you may want to have the output go to somewhere else but stdout. Hence, it might be useful to have a variant of the `trace' that takes an explicit file handle in the library.
Is there a list of problems anywhere with using trace? For example does it affect evaluation order? (I'm not sure of the semantics of unsafePerformIO!) The output from "trace" goes to stderr by the way. Cheers, Andy
Ben Escoto wrote:
Hi all, does anyone have any tips on how to insert debugging or logging statements through a program? Here are two possibilities:
another thing I found quite useful is to add a component { .. , info :: Doc } to my data types, and then set its value at each function call: f x y = ( ... ) { info = parens $ fsep [ text "f", info x, info y ] } ( I figure this is sort of part of what some tools can do automatically? ) that way you always know who built what. and it's cheap - if you don't use this information, then it's never created (due to laziness). best regards, -- -- Johannes Waldmann, Tel/Fax: (0341) 3076 6479 / 6480 -- ------ http://www.imn.htwk-leipzig.de/~waldmann/ ---------
Johannes Waldmann <waldmann@imn.htwk-leipzig.de> writes:
f x y = ( ... ) { info = parens $ fsep [ text "f", info x, info y ] }
Cool!
that way you always know who built what. and it's cheap - if you don't use this information, then it's never created (due to laziness).
Uh..is that really true? I would think it would keep a lot of data from being garbage collected? -kzm -- If I haven't seen further, it is by standing in the footprints of giants
participants (7)
-
Andy Fugard -
Ben Escoto -
Iavor S. Diatchki -
Johannes Waldmann -
ketil+haskell@ii.uib.no -
Manuel M T Chakravarty -
Peter Simons