Summary:
In order to use HOOD, you need to got through a number of steps.
Caveat: If you want to look at your own data structures, you need to write some instances. We explain this later, in Section 6.
We now introduce our debugging combinator, and explain how to run a program inside HOOD.
The basic idea behind HOOD is to have a combinator that allows the Haskell user to observe data structures in a transparent way. As a way of achieving this, consider the Haskell fragment:
consumer . producer
We provide a combinator for debugging that both passing an argument transparently, and observes and remembers the argument. To facilitate multiple observations in one program, we use a string argument, which is a label used only for identification purposes. The type of our principal debugging combinator is
|
observe :: (Observable a) => String -> a -> a |
In the above point-free example, we could write:
consumer . observe "intermediate" . producer
This has identical semantics to "consumer . producer", but the observe squirrels away the data structure that gets drawn through it, putting it into some persistent structure for later perusal. As far as the execution of Haskell program is concerned, observe (with a label) is just a version of id.
observe sees intermediate structures! Here is a table illustrating two ways to use observe. Note that the type and strictness properties of the expression before and after inserting observe is the same (modulo the Observe class restriction).
| Original Expression | New Expression | Intermediate Type | |
|---|---|---|---|
| Data structure observation |
<consumer> · <producer> | <consumer> · observe "intermediate list" · <producer> | <producer> :: ... -> A |
| map f · map g | map f · observe "between map f and map g" · map g | A | |
| <consumer> <producer> | <consumer> (observe "intermediate list" <producer>) | <producer> :: A | |
| map f (map g xs) | map f (observe "between map f and map g" (map g xs)) | A |
observe has a type class restriction on the object being observed. This does not turn out to be as big a problem as might be thought. HOOD provides instances for all the Haskell98 base types (Int, Bool, Float, etc), as well as many containers (List, Array, Maybe, Tuples, etc) so the member restriction should not be a problem in practice.
| Base Types | Int, Bool, Float, Double, Integer, Char,() |
|---|---|
| Constructors | (Observe a) => [a] and (Maybe a) |
| (Observe a,Observe b) => (a,b) and (Array a b) and Either a b | |
| (Observe a,Observe b,...) => 3-tuple, 4-tuple, 5-tuple. | |
| Functions | (Observe a,Observe b) => (a -> b) |
| IO Monad | (Observe a) => IO a |
| "Extended" Haskell | Exceptions (calls to error, etc) |
You can also add your instances for your own data structures. (The support for observations on Exceptions, which is useful for finding bottoms in your structures, is only supported on some compilers).
Lets construct a trivial example.
example :: IO ()
example = print (sum [1..10::Int])
We want to observe the list inside this program. So we write
example :: IO
()
example = print (observe "list" (sum [1..10::Int]))
(we've underlined the inserted code). We run this short program using the function runO.
runO :: IO a -> IO ()
So, our main would be
main :: IO ()
main = runO example
(Alternatively, in Hugs, you could just type "runO example"). Running this program give
prompt% ./main
55
-- list
( 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10 : [])
In summary