Thankfully, there's a number of solutions.
The GHCi debugger provides a way of inspecting code.
:b N
sets a break point in the loaded module at a specific line. Taking the anagrams example I set :b 30
on anagrams <-anagramList wordfile
.
anagramsOf :: String -> IO ()
anagramsOf word = do
anagrams <- anagramList wordfile
putStrLn (show (Map.lookup (stringToKey word) anagrams))
When the program is run and the breakpoint is hit we get the following:
Stopped at anagrams.hs:31:2-57
_result :: IO () = _
anagrams :: Map String (Set String) = _
word :: String = _
[anagrams.hs:31:2-57] *Main> :list
30 anagrams <- anagramList wordfile
31 putStrLn (show (Map.lookup (stringToKey word) anagrams))
32
:list
is used to list of the free variables in scope (anagrams
and word
) and these are available to inspect in the debugger. _result
is a binding for the result expression. Once you've hit a breakpoint, you can use use :trace
to continue to the next breakpoint, recording the history as you go along. :back
and :forward
allow you to go up and down the list of evaluated expressions and inspect each one.The Haskell Wiki has a section devoted to debugging which brought me to the Debug.Trace module. This allows you to print some text and return the evaluation of the next expression e.g.
Debug.Trace.trace "1+1=" (1 + 1)
.Initially, I couldn't understand how the type of
Debug.Trace.trace
could be String -> a -> a
, but then I found System.IO.Unsafe
. It comes with a large health warning that it doesn't enforce ordering on IO and it's type unsafe. Evil, yet useful (at least to write the trace functions).