
Konrad Hinsen
Particular difficulties in Haskell: - Conditional tracing. Suppose a function is called 1000 times but I am interested in a particular intermediate result only when the third argument is greater then three. - Tracing a part of a value, say the first five elements of a list or the even-numbered elements of an array.
The Hat solution is to trace everything, and then use a specialised query over the trace to narrow it to just the points you are interested in. At the moment, the hat-observe browser is the nearest to what you want - like HOOD, it permits you to see the arguments and results of a named function call, but additionally you can restrict the output to a named context, narrowed by normal Haskell-style patterns for the arguments and results. e.g. foo True [2,3,_] = Branch _ (Leaf _) in g asks for all applications of foo called from g, where foo's first argument is True, its second argument is a list of three elements beginning with 2 and 3, and the result of the call is a tree with a leaf on the immediate right of the top-most branch. Over the next year, we hope to have a research student looking at extending the query language of hat-observe. For instance, I can imagine adding Haskell-style guards, which would make it possible to express your first example as foo _ _ x | x > 3 -- third argument must be greater than three Another idea is to permit real Haskell expressions to post-process the result of the trace query, rather like meta-programming. So your second example might look something like take 5 [| foo 3 |] -- show only the first five elements of the result list evens [| mkarray _ |] -- select and print only the even-indexed array elements where evens arr = show (map (\i->arr!i) [b, succ(succ(b)) .. t]) where b = fst (bounds arr) t = snd (bounds arr)
If it takes complex data structures as input, then the only reasonable way to provide that input may be calling it from another piece of the code.
Exactly, and this is where Hat is most useful, because it traces the real run of the program, recording all the intermediate data structures.
QuickCheck gives a much better indication of correctness with much less manual labor.
I can recommend QuickCheck as well, although Koen or John may wish to comment on how suitable they think it would be for numerical work. QuickCheck and Hat can be made to work together nicely. There is a version of QuickCheck in development which works by first running the ordinary program with lots of random test data. If a failure is found, it prunes the test case to the minimal failing case, and passes that minimal case to a Hat-enabled version of the program, which can then be used to investigate the cause of the failure.
the type level). Do you want to avoid ANY code changes?
Not necessarily, but I'd prefer them to be local, at least restricted to one module. I ended up introducing (minor) bugs into rather unrelated code through typos made when adding "Observable" constraints.
The need to alter the program under test is the biggest disadvantage of HOOD in my opinion since, as you have shown, it can lead to new bugs. Hat largely avoids this pitfall. I believe there is also a version of HOOD more fully integrated with Hugs, which removes the need to derive Observable instances and add extra Observable constraints on functions. Regards, Malcolm