Debugging Haskell by Observing Intermediate Data Structures

Slides:



Advertisements
Similar presentations
Type Checking, Inference, & Elaboration CS153: Compilers Greg Morrisett.
Advertisements

Computer Science 2212a/b - UWO1 Structural Testing Motivation The gcov Tool An example using gcov How does gcov do it gcov subtleties Further structural.
1/20 Generalized Symbolic Execution for Model Checking and Testing Charngki PSWLAB Generalized Symbolic Execution for Model Checking and Testing.
Getting started with ML ML is a functional programming language. ML is statically typed: The types of literals, values, expressions and functions in a.
Functional Programming Universitatea Politehnica Bucuresti Adina Magda Florea
FIT FIT1002 Computer Programming Unit 19 Testing and Debugging.
GHood Visualising Observations of Haskell Program Runs Claus Reinke Computing Lab, University of Kent at Canterbury.
1 CSE1301 Computer Programming: Lecture 15 Flowcharts and Debugging.
Advanced Programming Handout 12 Higher-Order Types (SOE Chapter 18)
Introduction to Computers and Programming Lecture 5 Boolean type; if statement Professor: Evan Korth New York University.
1 CSE1301 Computer Programming: Lecture 15 Flowcharts, Testing and Debugging.
Using Types Slides thanks to Mark Jones. 2 Expressions Have Types: The type of an expression tells you what kind of value you might expect to see if you.
0 PROGRAMMING IN HASKELL Typeclasses and higher order functions Based on lecture notes by Graham Hutton The book “Learn You a Haskell for Great Good” (and.
Haskell. 2 GHC and HUGS Haskell 98 is the current version of Haskell GHC (Glasgow Haskell Compiler, version 7.4.1) is the version of Haskell I am using.
A Lightweight Interactive Debugger for Haskell Simon Marlow José Iborra Bernard Pope Andy Gill.
Testing and Debugging Version 1.0. All kinds of things can go wrong when you are developing a program. The compiler discovers syntax errors in your code.
Advanced Functional Programming 2009 Ulf Norell (lecture by Jean-Philippe Bernardy)
Copyright © 2010 Certification Partners, LLC -- All Rights Reserved Perl Specialist.
© M. Winter COSC 4P41 – Functional Programming Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there.
1 Chapter 3 Syntax, Errors, and Debugging Fundamentals of Java: AP Computer Science Essentials, 4th Edition Lambert / Osborne.
0 Odds and Ends in Haskell: Folding, I/O, and Functors Adapted from material by Miran Lipovaca.
Java Basics Hussein Suleman March 2007 UCT Department of Computer Science Computer Science 1015F.
Debugging Logic Errors CPS120 Introduction to Computer Science.
CS Class 04 Topics  Selection statement – IF  Expressions  More practice writing simple C++ programs Announcements  Read pages for next.
1 CSE1301 Computer Programming: Lecture 16 Flow Diagrams and Debugging.
0 PROGRAMMING IN HASKELL Typeclasses and higher order functions Based on lecture notes by Graham Hutton The book “Learn You a Haskell for Great Good” (and.
Advanced Functional Programming 2010
Spring 2017 Program Analysis and Verification
Polymorphic Functions
Laziness and Infinite Datastructures
Programming in visual basic .net Visual Basic Building Blocks
User-Written Functions
Programming Languages
dr Robert Kowalczyk WMiI UŁ
Types CSCE 314 Spring 2016.
John D. McGregor Session 9 Testing Vocabulary
Theory of Computation Lecture 4: Programs and Computable Functions II
CS 326 Programming Languages, Concepts and Implementation
Representation, Syntax, Paradigms, Types
Chapter 2 Assignment and Interactive Input
A lightening tour in 45 minutes
Haskell.
CSE 3302 Programming Languages
Lecture 07 More Repetition Richard Gesick.
Eclipse 20-Sep-18.
John D. McGregor Session 9 Testing Vocabulary
PROGRAMMING IN HASKELL
Programming Languages
Haskell Dealing with impurity 30-Nov-18.
Programming Fundamentals (750113) Ch1. Problem Solving
Programming Languages and Compilers (CS 421)
PROGRAMMING IN HASKELL
Representation, Syntax, Paradigms, Types
Sridhar Narayan Java Basics Sridhar Narayan
Type & Typeclass Syntax in function
Representation, Syntax, Paradigms, Types
Homework Any Questions?.
Representation, Syntax, Paradigms, Types
Records and Type Classes
Programming Languages
Introduction to Programming
Javascript Chapter 19 and 20 5/3/2019.
Functional Programming and Haskell
Theory of Computation Lecture 4: Programs and Computable Functions II
An Overview of C.
PROGRAMMING IN HASKELL
ICS103: Programming in C 5: Repetition and Loop Statements
Functional Programming and Haskell
Records and Type Classes
Presentation transcript:

Debugging Haskell by Observing Intermediate Data Structures Andy Gill Galois Connections andy@galconn.com www.cse.ogi.edu/~andy

Debugging 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

Traditional 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

Tracer 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

Algorithmic 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

Work 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

Understanding Haskell execution natural :: Int -> [Int] natural = reverse  . map (`mod` 10) . takeWhile (/= 0)  . iterate (`div` 10) Main> natural 3408 [3,4,0,8] :: [Int] 3 : 4 : 0 : 8 : [] 8 : 0 : 4 : 3 : [] 3408 : 340 : 34 : 3 : [] 3408 : 340 : 34 : 3 : 0 : … 3408

The 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…

Using trace for debugging Function called trace trace :: String -> a -> a Print the string to stderr & return the second argument All Haskell systems have this function

trace can help… foo (x:xs) (y:xs) = … foo [] [] = … Can rewrite this as foo x y | trace (“foo:” ++ show (x,y)) False = undefined

Problems with trace Very Bad News The best we had – until now… Invasive to your code foo (n + 1) => let r = foo (n + 1) in trace (show(n+1,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…

Our debugging combinator Provide a way of annotating a specific expression with a label. observe :: (Observable a) => String -> a -> a boring = observe “after” . reverse . observe “during” . observe “before” Similar to probe in Hawk, but works over most data types.

First example printO :: Show a => a -> IO () Main> printO (take 2 (observe “list” [1..10])) printO :: Show a => a -> IO () Opens the debugging context evaluates the argument with possible observations made Closes the debugging context, pretty prints observed structures. ( 1 : 2 : _)

Second example ( 1 : 2 : _) printO ( let lst = [1..10] lst1 = take 2 (observe “list” lst) in sum (lst ++ lst1) ) ( 1 : 2 : _)

Annotating natural Focuses on the intermediate data structures 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

Output 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 : [] )

What can we observe? Base types Structured 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) => String -> (a -> b) -> a -> b What does this definition mean?

Observing 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

Example of function observation Main> printO (map (observe "null" null) [[],[1..]]) -- null { \ [] -> True , \ (_: _) -> False } Shows only the specific invocations used.

Example of higher order function observation Main> printO $ (observe “map null” map null [[],[1..]]) -- map null { \ { \ [] -> True , \ (_ : _) -> False } ([] : (_ : _) : []) -> (True : False : [])

Debugging 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

Output from debugging natural -- iterate (`div' 10)  \ { \ 3408 -> 340    , \ 340 -> 34     , \ 34 -> 3     , \ 3 -> 0     }     3408 -> (3408 : 340 : 34 : 3 : 0 : _) -- takeWhile (/= 0) \ { \ 3408 -> True   , \ 340 -> True    , \ 34 -> True     , \ 3 -> True     , \ 0 -> False      (3408 : 340 : 34 : 3 : 0 : _) -> ( 3408 : 340 : 34 : 3 : [] )

Bad implementation of observe observe :: (Show a) => String -> a -> a observe label a = trace (label ++ “:” ++ show a) a We’ve 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.

How does lazy evaluation work? p => (:) p,2 => (:) p,2,2 => [] p p,1 p,2 p,2,1 p,2,2 (:) Thunk Thunk (:) Thunk (:) Thunk []

Structural observers class Observer a where observer :: a -> (PATH,LABEL) -> a -- Example for (a,b) instance (Observer a,Observe b) => Observe (a,b) where -- observer :: (a,b) -> (PATH,LABEL) -> (a,b) observer (a,b) (path,label) = unsafePerformIO do { sendPacket “… (,) … path … label … ” ; return (observer a (1 : path,label), observer b (2 : path,label)) }

Systematic side effects fst (observe “…” (f x,44)) = fst (observer (f x,44) cxt) = fst (unsafe $ do { “tell about tuple” ; return (observe (f x) (1:cxt),observe 44 (2:cxt)) }) -- I’m a 2-tuple at path “cxt” ( _ , _ ) = fst (observe (f x) (1:cxt),observe 44 (2:cxt)) = observe (f x) (1:cxt) = seq (f x) $ (unsafe $ do { “tell about the number” ; return (f x) }) = unsafe $ do { “tell about the number” ; return 99 } -- I’m an 99 at path “1 : cxt” ( 99 , _ ) = 99

Status 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 NHC’s version) Polymorphic observations (using RTS hooks) Operational semantics for the debugger? Extensions GHOOD – Shows trees instead of pretty printed Haskell. Quickcheck – for showing counterexamples.

Conclusions 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

Demo … 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

Homework 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])