Title: Debugging Haskell by Observing Intermediate Data Structures
1Debugging Haskell by Observing Intermediate Data
Structures
- Andy Gill
- Galois Connections
- andy_at_galconn.com
- www.cse.ogi.edu/andy
2Debugging any program
- Debugging is locating a misunderstanding
- Finding the difference between
- What you told the computer to do
- What you think you told the computer to do
- Involves running code
- Sometimes about finding out what someone else
told the computer to do - A Debugger must never, never lie
- You are gathering evidence, the debugger is under
oath
3Traditional debugging tools
- The printf debuggers
- Insert printf statements into code
- Generally the dump and inspect approach
- Written Statements
- CLI or GUI Interactive Debuggers
- GDB
- Visual Studio
- Witnesses
4Tracer Debuggers
- Trace reduction steps, producing extra output
- Present dynamically the internal reduction graph
- Two ways of getting the trace both compiler
specific - Instrumenting the code via transformations
- Use a modified internal reduction interpreter
- There is another way ?
- Two major technical problems
- The size of the traces can be huge
- The structures that the user might browse are
also huge
5Algorithmic Debugging
- Also called declarative debugging
- Semi-automated binary search
- The system asks Yes/No questions, starting at
main - If a function is giving incorrect results
- One of the sub functions must be faulty
- Or this function must be faulty
- Can use oracles to good effect
- Expensive asserts
- Strange interactions with IO
6Work on Haskell Debuggers
- Tracing
- ART/HAT project York (nhc)
- HOOD OGI
- GHood, HOOD in NHC, HOOD in Hugs.
- Declarative
- Freja Hendrik Nilsson
- Budda Melbourne, Australia
- Testing Frameworks
- Quickcheck Chalmers
- Auburn York
- Look at www.haskell.org/debugging
7Understanding Haskell execution
- natural Int -gt Int
- natural
- reverseÂ
- . map (mod 10)
- . takeWhile (/ 0)Â
- . iterate (div 10)
- Maingt natural 3408
- 3,4,0,8 Int
8The Haskell Object Observation Debugger
- Provide combinators for debugging
- STG Hugs or GHC or NHC (others will follow)
- Frustrated Haskell programmer uses combinators to
annotate their code, and re-runs the program - The execution of the Haskell program allows
observations to be made internally - At the termination of the Haskell execution,
observed structures are displayed to stderr in a
pretty printed form - Think written statements, but not witnesses
9Using trace for debugging
- Function called trace
- trace String -gt a -gt a
- Print the string to stderr
- return the second argument
- All Haskell systems have this function
10trace can help
- foo (xxs) (yxs)
- foo
- Can rewrite this as
- foo x y
- trace (foo show (x,y)) False
undefined - foo (xxs) (yxs)
- foo
11Problems with trace
- Invasive to your code
- foo (n 1) gt let r foo (n 1)
- in trace (show(n1,r)) r
- Evaluation of first argument can cause other
traces to fire, mid trace. - End up with a spaghetti output
- Perhaps trace should be evaluate its argument?
- Can change the evaluation order of your program!
- Very Bad News
- The best we had until now
12Our debugging combinator
- Provide a way of annotating a specific expression
with a label. - observe (Observable a) gt String -gt a -gt a
- boring observe after
- . reverse
- . observe during
- . reverse
- . observe before
- Similar to probe in Hawk, but works over most
data types.
13First example
- Maingt printO (take 2 (observe list 1..10))
- printO Show a gt a -gt IO ()
- Opens the debugging context
- evaluates the argument
- with possible observations made
- Closes the debugging context, pretty prints
observed structures.
( 1Â Â 2Â Â _)
14Second example
- printO (
- let lst 1..10
- lst1 take 2 (observe list lst)
- in
- sum (lst lst1)
- )
( 1Â Â 2Â Â _)
15Annotating natural
- Add observe annotations to natural, capturing the
flow between the subcomponents. - natural
- reverse
- . observe "after map
- . map (mod 10)
- . observe "after takeWhile
- . takeWhile (/ 0)
- . observe "after iterate
- . iterate (div 10)
- Focuses on the intermediate data structures
16Output from debugging natural
- -- after iterate (div 10)
- (3408 340 34 3 0 _)
-
- -- after takeWhile (/ 0)
- ( 3408 340 34 3 )
-
- -- after map (mod 10)
- ( 8 0 4 3 )
-
17What can we observe?
- Base types
- Int, Char, Integer, Bool, Float, Double, ()
- Structured Types
- Tuples, Lists, Maybe, Arrays
- Exceptions?
- What about Monads? (List and Maybe are Monads).
- What about Functions?
- observe (Observe a,Observe b) gt String -gt (a
-gt b) -gt a -gt b - What does this definition mean?
18Observing functions
- A observed function
- Only called a specific arguments
- Finite map from argument to result
- For observational purposes, functions are
- A set of pairs, representing argument and result
- Isomorphic to (a,b)
- Is this a Set or a Bag?
- We can pretty print in a Haskell like manner
19Example of function observation
- Maingt printO (map (observe "null" null)
,1..) - -- null
- \ -gt True
- , \ (_ _) -gt False
-
- Shows only the specific invocations used.
20Example of higher order function observation
- Maingt printO
- (observe map null map null ,1..)
- -- map null
- \ \ -gt True
- , \ (_ _) -gt False
-
- ( (_ _) )
- -gt (True False )
21Debugging natural (again)
- Now use HO debugging
- natural
- . observe reverse reverse
- . observe "map (mod 10) map (mod 10)
- . observe "takeWhile (/ 0) takeWhile (/ 0)
- . observe "iterate (div 10) iterate (div 10)
- Focus is now what the sub-components do
22Output from debugging natural
- -- iterate (div' 10)Â
- \  \ 3408 -gt 340
-   , \ 340 -gt 34
-    , \ 34 -gt 3
-     , \ 3 -gt 0
- Â Â Â Â
-     3408 -gt (3408  340  34  3  0  _)
- -- takeWhile (/ 0)
- \  \ 3408 -gt True
-   , \ 340 -gt True
-   , \ 34 -gt True
-    , \ 3 -gt True
-     , \ 0 -gt False
- Â Â Â Â
-      (3408  340  34  3  0  _) -gt ( 3408  340
  34  3 )
23Bad implementation of observe
- observe (Show a) gt String -gt a -gt a
- observe label a
- trace (label show a) a
- Weve changed the strictness of the second
argument - Would fail on our natural example, because one of
the intermediate lists is infinite - Needs to be a member of the class Show
- Functions are not Show-able.
24How does lazy evaluation work?
Thunk
25Structural observers
- class Observer a where
- observer a -gt (PATH,LABEL) -gt a
- -- Example for (a,b)
- instance (Observer a,Observe b) gt Observe (a,b)
where - -- observer (a,b) -gt (PATH,LABEL) -gt (a,b)
- observer (a,b) (path,label)
- unsafePerformIO
- do sendPacket (,) path label
- return (observer a (1 path,label),
- observer b (2 path,label))
-
26Systematic side effects
- fst (observe (f x,44))
- fst (observer (f x,44) cxt)
- fst (unsafe do tell about tuple
- return (observe (f x) (1cxt),observe
44 (2cxt)) - )
- -- Im a 2-tuple at path cxt ( _ , _ )
- fst (observe (f x) (1cxt),observe 44 (2cxt))
- observe (f x) (1cxt)
- seq (f x) (unsafe do tell about the
number - return (f x) )
- unsafe do tell about the number
- return 99
- -- Im an 99 at path 1 cxt ( 99 , _ )
- 99
27Status and future of HOOD
- Current Status
- V0.1 - Available on web www.haskell.org/hood
- Works with Hugs, GHC, STG Hugs, NHC
- Handles GHC threads fine
- Can catch and observe exceptions (errors, C,
etc). - -- list
- ( 1 2 error boom _ )
- The Future
- V0.2 Interactive browser via XML file (already
in NHCs version) - Polymorphic observations (using RTS hooks)
- Operational semantics for the debugger?
- Extensions
- GHOOD Shows trees instead of pretty printed
Haskell. - Quickcheck for showing counterexamples.
28Conclusions
- Haskell has something resembling a debugging tool
that works on real Haskell - Type classes can be used to augment a data
structure evaluation with side-effecting
functions - Observation of non-trivial examples is possible
29Demo
import Observe n 10 x1 foldr () 0 1..n y1
foldr (observe "Add" ()) 0 (observe "input"
1..n) z1 printO y1 x2 foldl () 0
1..n x3 foldr () 0 (reverse 1..n) y3
foldr () 0 (take 4 (observe "revlist"
(reverse (observe "input"
1..n)))) z3 printO x3 x4 foldl () 0
(reverse 1..n) y4 foldl () 0 (observe
"revlist" (reverse
(observe "input" 1..n))) z4 printO y4
30Homework
- Download HOOD
- Download HOOD documentation ?
- Investigate the following
- foldr () 0 1..n
- foldl () 0 1..n
- foldr () 0 (reverse 1..n)
- foldl () 0 (reverse 1..n)