putTraceMsg :: String -> IO () trace :: String -> a -> a
I chose to go with putTaceMsg... because what the heck is that a -> a all about? Okay, so I plopped it into my code and voila, it compiles and I get to see my output. A day later I added it to a different chunk of code and it won't compile.
I was frustrated and confused. Then I noticed that it worked in an IO() context, but not in a pure function... Hmm.
I got a clue from reading totherme's post Development with Types and it's comments. I re-read chapters 4 and 7 of RWH focusing on Types, I see that I've been having trouble internalizing "everything is an expression. I need to unlearn the imperative statement oriented languages.
I think I'm starting to see what the basis is for where I can use trace. I've got to focus on the "shape" of the main function or within my pure functions. Like lego bricks, the input Type and output Type of each component must match. The overall type must be satisfied by the expression. An expression can be broken up by if, let, where, case, and function calls. The various branches, must all have the same type. Doh, but realizing this difference from weakly typed, statement oriented languages really takes some time.
Okay, so now I understand why trace has the signature String -> a -> a. This allows you to "hide" your tracing by not changing the Type of your expression. You are effectively wrapping a sub-expression in a trace.
Example: To inject a bit of tracing, just wrap an expression in trace. Before and after, it retains the same shape:
trace "hello world" (some f)
Related learning, while trying to figure this out: I've been avoiding declaring the Type signature of functions, until I had them "figured out". It sounds like I need to go the other way around, so that I'll be sure that the pile of function calls maintains the correct shape.