|
|
|
|
Development of Hat was funded by grant number GR/M81953 from the Engineering and Physical Sciences Research Council of the United Kingdom.
Hat is a source-level tracer for Haskell 98, the standard lazy functional programming language. Hat is a tool that gives the user access to otherwise invisible information about a computation.
Hat is portable between compilers: it works with both ghc and nhc98. It also supports various language extensions, such as the FFI, multi-parameter type classes, functional dependencies, and hierarchical module namespaces.
Hat helps locating errors in programs. Furthermore, it is useful for understanding how a (correct) program works, especially for teaching and program maintenance. Hat is not a time or space profiler. Hat can be used for programs that terminate normally, that terminate with an error message or that terminate when interrupted by the programmer.
Tracing a program with Hat consists of two phases: First the specially compiled program runs as normal, except that additionally a trace is written to file. Second, after the program has terminated, the trace is viewed with a browsing tool. The trace consists of high-level information about the computation. It describes each reduction, that is, the replacements of an instance of a left-hand side of an equation by an instance of its right-hand side, and the relation of the reduction to other reductions. Because the trace describes the whole computation, it is huge. Hat comes with several tools to selectively view the fragments of the trace that are of interest. Each tool shows fragments of the computation in a particular way, highlighting a specific aspect.
All tools show function arguments in evaluated form, more precisely: as far evaluated as the arguments are at the end of the computation. For example, although in a computation the unevaluated expression (map (+5) [1,2]) might be passed to the function length, the tools will show the function application as length [1+5,2+5] or length [_,_].
For example, the computation of the faulty program
main = let xs :: [Int] xs = [4*2,5 `div` 0,5+6] in print (head xs,last' xs) last' (x:xs) = last' xs last' [x] = xgives the result
(8, No match in pattern.and the Hat viewing tools can be used to explore its behaviour as follows:
$ hat-observe Example hat-observe 2.04 (:h for help, :q to quit) hat-observe> main 1 main = IO (print (8,_|_)) hat-observe> print 1 print (8,_|_) = IO (print (8,_|_)) hat-observe> last' 1 last' [8,_,_] = _|_ 2 last' [_,_] = _|_ 3 last' [_] = _|_ 4 last' [] = _|_ hat-observe> :quit $
Every reduction replaces an instance of the left-hand side of a program equation by an instance of its right-hand side. The instance of the left-hand side ``creates'' the instance of the right-hand side and is therefore called its parent. With hat-trail you can obtain the parent of any expression.
(Note: if you cannot see any highlighting in the following diagram, try changing the fixed-width font in your browser to something like Courier text.) Each line of the trail is the parent of the highlighted subexpression directly above it.
Error: ------------------------------------------------------- No match in pattern. Output: ------------------------------------------------------ (8, Trail: ---------------------- Example.hs line: 2 col: 12 ----- <- last' [] <- last' [_] <- last' [_,_] <- last' [8,_,_] <- 4 * 2 <- xs
Here, the error message is chosen as the starting point, rather than any of the output. The first trail is therefore last' [], because its evaluation caused the error message. The parent of last' [] is last' [_]. The parent of last' [_] is last' [_,_]), etc. The parent of the subexpression 8 is 4*2 whose parent is xs.
Because arguments and the result are shown for each function call, it is easier to determine which function is incorrect. You can also mark reductions as correct/incorrect which enables the tool to pinpoint the bug to a smaller and smaller slice of the program.
==== Hat-Explore 2.04 ==== Press h for help. =================== 1. main = {IO} 2. last' [8,_,_] = _|_ 3. last' [_,_] = _|_ 4. last' [_] = _|_ 5. last' [] = _|_ ---- Last.hs ---- lines 1 to 7 --------------------------------- main = let xs :: [Int] xs = [4*2,5 `div` 0,5+6] in print (head xs,last' xs) last' (x:xs) = last' xs last' [x] = x
Example session (y/n answers are given by the user):
$ hat-detect Example hat-detect 2.0x (:h for help, :q to quit) 1 main = IO (print [3,3,3]) ? n 2 sort [3,2,1] = [3,3,3] ? n 3 insert 1 [] = [1] ? y 4 insert 2 [1] = [2,2] ? n 5 insert 2 [] = [2] ? y Error located! Bug found in reduction: insert 2 [1] = [2,2]
Using the same example program as above, hat-stack shows
$ hat-stack Example Program terminated with error: No match in pattern. Virtual stack trace: (Last.hs:6) last' [] (Last.hs:6) last' [_] (Last.hs:6) last' [_,_] (Last.hs:4) last' [8,_,_] (unknown) main $
If you have any problems, please send a message to the mailing list at hat@haskell.org.
The latest updates to these pages are available on the WWW from
http://haskell.org/hat/
http://www.cs.york.ac.uk/fp/hat/
This page last modified: 2nd Oct 2008
York Functional Programming Group