Missteps of a Narwhal Calf

Previous Entry Share Next Entry
Where can I put my debug statements?
king, avatar, owl, ozten, austin
I'm not much on debuggers and rely on the REPL or println statements to learn what a piece of code is doing (or not doing). A frustration I've been having when learning Haskell, is not knowing where I could use Debug.Trace's trace function in my code. The module Debug.Trace provides two methods.
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:
  some f
  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.

  • 1

Definitely go with the types!

I too am just learning Haskell. Definitely write the types in for functions, otherwise you end up with the wrong types being inferred and functions not compiling with indecipherable messages. If you put the right types in first of all, you can't go too far wrong!

Re: Definitely go with the types!

Good point! I was re-reading Chapter 5 of RWH and they were talking about exactly this issue.

It's a facepalm moment once something compiles just because you add a type signature...
(Frozen) (Parent) (Thread)

  • 1

Log in