slides-detecting pattern match failures in haskell-26 nov 2007
TRANSCRIPT
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
1/59
Detecting Pattern-Match Failures
in Haskell
Neil Mitchell andColin Runciman
York University
www.cs.york.ac.uk/~ndm/catch
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
2/59
Does this code crash?
risers [] = []
risers [x] = [[x]]
risers (x:y:etc) =ifx y then (x:s) : ss else [x] : (s:ss)
where (s:ss) = risers (y:etc)
> risers [1,2,3,1,2] = [[1,2,3],[1,2]]
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
3/59
Does this code crash?
risers [] = []
risers [x] = [[x]]
risers (x:y:etc) =ifx y then (x:s) : ss else [x] : (s:ss)
where (s:ss) = risers (y:etc)
> risers [1,2,3,1,2] = [[1,2,3],[1,2]]
Potential crash
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
4/59
Does this code crash?
risers [] = []
risers [x] = [[x]]
risers (x:y:etc) =ifx y then (x:s) : ss else [x] : (s:ss)
where (s:ss) = risers (y:etc)
> risers [1,2,3,1,2] = [[1,2,3],[1,2]]Potential crash
Property:risers (_:_) = (_:_)
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
5/59
Overview
zThe problem of pattern-matching
z A framework to solve patterns
z Constraint languages for the frameworkzThe Catch tool
z
A case study: HsColourz Conclusions
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
6/59
The problem of Pattern-Matching
head (x:xs) = x
head x_xs =case x_xs ofx:xs x
[] error head []
z Problem: can we detect calls to error
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
7/59
Haskell programs go wrong
z Well-typed programs never go wrong
z But...
Incorrect result/actions requires annotations Non-termination cannot always be fixed
Call error not much research done
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
8/59
My Goal
z Write a tool for Haskell 98 GHC/Haskell is merely a front-end issue
z Check statically that error is not called Conservative, corresponds to a proof
z Entirely automatic No annotations
= Catch
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
9/59
Preconditions
z Each function has a precondition
z If the precondition to a function holds, and
none of its arguments crash, it will not crash
pre(head x) = x {(:) _ _}
pre(assertx y) = x {True}
pre(null x) = True pre(error x) = False
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
10/59
Properties
z A property states that if a function is calledwith arguments satisfying a constraint, the
result will satisfy a constraint
x {(:) _ _} (null x) {True}
x {(:) [] _} (head x) {[]}
x {[]} (head x) {True}
Calculation direction
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
11/59
Checking a Program (Overview)
z Start by calculating the precondition of main If the precondition is True, then program is safe
z Calculate other preconditions and propertiesas necessary
z Preconditions and properties are definedrecursively, so take the fixed point
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
12/59
Checking risers
risers r =case r of
[] []
x:xs
case xs of[] (x:[]) : []
y:etc case risers (y:etc) of
[] error pattern matchs:ss case x y of
True (x:s) : ss
False [x] : (s:ss)
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
13/59
Checking risers
risers r =case r of
[] []
x:xs
case xs of[] (x:[]) : []
y:etc case risers (y:etc) of
[] error pattern matchs:ss case x y of
True (x:s) : ss
False [x] : (s:ss)
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
14/59
Checking risers
risers r =case r of
[] []
x:xs
case xs of[] (x:[]) : []
y:etc case risers (y:etc) of
[] error pattern matchs:ss case x y of
True (x:s) : ss
False [x] : (s:ss)
r {[]} xs {[]}
risers (y:etc)
{(:) _ _}
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
15/59
Checking risers
risers r =case r of
[] []
x:xs
case xs of[] (x:[]) : []
y:etc case risers (y:etc) of
[] error pattern matchs:ss case x y of
True (x:s) : ss
False [x] : (s:ss) ... [x] : (s:ss) {(:) _ _}
... (x:s) : ss {(:) _ _}
... {(:) _ _}
... (x:[]) : []
{(:) _ _}
r {(:) _ _}[] {(:) _ _}
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
16/59
Checking risers
risers r =case r of
[] []
x:xs case
xsof
[] (x:[]) : []
y:etc case risers (y:etc) of
[] error pattern matchs:ss case x y of
True (x:s) : ss
False [x] : (s:ss) ... [x] : (s:ss) {(:) _ _}
... (x:s) : ss {(:) _ _}
... {(:) _ _}
... (x:[]) : []
{(:) _ _}
r {(:) _ _}[] {(:) _ _}
Property:
r {(:) _ _}risers r {(:) _ _}
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
17/59
Checking risers
risers r =case r of
[] []
x:xs case
xsof
[] (x:[]) : []
y:etc case risers (y:etc) of
[] error pattern matchs:ss case x y of
True (x:s) : ss
False [x] : (s:ss)
r {[]} xs {[]}
risers (y:etc)
{(:) _ _}
Property:
r {(:) _ _}risers r {(:) _ _}
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
18/59
r {[]} xs {[]}
y:etc
{(:) _ _}
Checking risers
risers r =case r of
[] []
x:xs case
xsof
[] (x:[]) : []
y:etc case risers (y:etc) of
[] error pattern matchs:ss case x y of
True (x:s) : ss
False [x] : (s:ss)
Property:
r {(:) _ _}risers r {(:) _ _}
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
19/59
Calculating Preconditions
z Variables: pre(x) = True Always True
z Constructors: pre(a:b) = pre(a) pre(b) Conjunction of the children
z Function calls: pre(f x) = x pre(f) pre(x) Conjunction of the children
Plus applying the preconditions of f
Note: precondition is recursive
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
20/59
Calculating Preconditions (case)
pre(case on of
[] a
x:xs b)= pre(on) (on {[]} pre(a))
(on
{(:) _ _}
pre(b))
z An alternative is safe, or is never reached
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
21/59
Extending Constraints ()risers r =case r of
[] []
x:xs case xs of
[] (x:[]) : []
y:etc ...
xs {(:) _ _} ...r {(:) _ _}
r
{(:) _ ((:) _ _)}
{(:) _ _}{(:) _ ((:) _ _)}
{True}{(:) True _}
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
22/59
Splitting Constraints ()risers r =case r of
[] []
x:xs case xs of
[] (x:[]) : []
y:etc ...
(x:[]):[] {(:) _ _} ...True
((:) 1 2) {(:) _ _}True
((:) 1 2) {[]}False
((:) 1 2) {(:) True []}
1 {True} 2 {[]}
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
23/59
Summary so far
z Rules for Preconditions
z How to manipulate constraints
Extend () for locally bound variables Split () for constructor applications
Invoke properties for function application
z Can change a constraint on expressions, toone on function arguments
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
24/59
Algorithm for Preconditions
set all preconditions to True
set error precondition to False
while any preconditions changerecompute every precondition
end while
z Algorithm for properties is very similar
Fixed Point!
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
25/59
Fixed Point
zTo ensure a fixed point exists demand only afinite number of possible constraints
z At each stage, () with the previousprecondition
z Ensures termination of the algorithm But termination useable speed!
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
26/59
The Basic Constraints
zThese are the basic ones I have introduced
z Not finite but can bound the depth A little arbitrary
Cant represent infinite data structures
z But a nice simple introduction!
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
27/59
A Constraint System
z Finite number of constraints
z Extend operator ()
z Split operator ()z notin creation, i.e. x {(:) _ _)}
z
Optional simplification rules in a predicate
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
28/59
Regular Expression Constraints
z Based on regular expressions
z x r c
r is a regular expression of paths, i.e. c is a set of constructors
True if all r paths lead to a constructor in c
z Split operator () is regular expressiondifferentiation/quotient
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
29/59
RE-Constraint Examples
z head xs xs (1 {:})
z
map head xs xs (* {:})
z map head (reverse xs) xs (* {:})
xs (* {:})
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
30/59
RE-Constraint Problems
zThey are finite (with certain restrictions)
z But there are many of them!
z Some simplification rules Quite a lot (19 so far)
Not complete
z In practice, too slow for moderate examples
This fact took 2years to figure
out!
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
31/59
Multipattern Constraints
z Idea: model the recursive and non-recursivecomponents separately
z Given a list Say something about the first element
Say something about all other elements Cannot distinguish between element 3 and 4
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
32/59
MP-Constraint Examples
z head xs xs ({(:) _} {[], (:) _})
z Use the types to determine recursive bits
xs must be (:)
xs. must be _
All recursive tails
are unrestricted
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
33/59
More MP-Constraint Examples
z map head xs {[], (:) ({(:) _} {[], (:) _})}
{[], (:) ({(:) _} {[], (:) _})}
z An infinite list
{(:) _} {(:) _}
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
34/59
MP-Constraint semantics
MP = {set Val}
Val = _ | {set Pat} {set Pat}
Pat= Constructor [(non-recursive field, MP)]
Element must satisfy
at least one pattern
Each recursive part must
satisfy at least one pattern
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
35/59
MP-Constraint Split
z ((:) 1 2) {(:) _} {(:) {True}} An infinite list whose elements (after the first) are
all true
z 1 _
z 2 {(:) {True}} {(:) {True}}
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
36/59
MP-Constraint Simplification
zThere are 8 rules for simplification Still not complete...
z
But! x a x b = x c union of two sets
x a x b = x c cross product of two sets
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
37/59
MP-Constraint Currying
z We can merge all MPs on one variable
z We can curry all functions so each has only
one variablez MP-constraint Predicate MP-constraint
(||) a b (||) (a, b)
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
38/59
MP vs RE constraints
z Both have different expressive power Neither is a subset/superset
z RE-constraints grow too quickly
z MP-constraints stay much smaller
zTherefore Catch uses MP-constraints
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
39/59
Numbers
data Int = Neg | Zero | One | Pos
z Checks Is positive? Is natural? Is zero?
z Operations (+1), (-1)
z Works very well in practice
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
40/59
Summary so far
z Rules for Preconditions and Properties
z Can manipulate constraints in terms of three
operationsz MP and RE Constraints introduced
z Have picked MP-Constraints
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
41/59
Making a Tool (Catch)
Haskell
Core
First-order Core
Curried
Analyse
Yhc
In draft paper,see website
This talk
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
42/59
Testing Catch
zThe nofib benchmark suite, but
main =do [arg] getArgsprint $ primes !! (read arg)
z Benchmarks have no real users
z Programs without real users crash
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
43/59
Nofib/Imaginary Results (14 tests)
Trivially Safe
Perfect Answer
Good Failu res
Bad Failures
Good failure:Did not get perfect answer,
but neither did I!
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
44/59
Bad Failure: Bernouilli
tail (tail x)
z Actual condition: list is at least length 2z Inferred condition: list must be infinite
drop 2 x
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
45/59
Bad Failure: Paraffins
radical_generator n = f undefinedwhere f unused = big_memory_result
z array :: Ix a (a, a) [(a, b)] Array a b Each index must be in the given range
Array indexing also problematic
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
46/59
Perfect Answer: Digits of E2
e =(2.++) $
tail concat $map (show head) $
iterate (carryPropagate 2 map (10*) tail) $
2 : [1,1 ..]
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
47/59
Performance of Catch
0
1
2
3
45
6
7
8
0 200 400 600 800 1000 1200 1400
Source Code
Time(Seco
nds)
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
48/59
Case Study: HsColour
zTakes Haskell source code and prints out acolourised version
z
4 years old, 6 contributors, 12 modules, 800+lines
z Used by GHC nightly runs to generate docsz Used online by http://hpaste.org
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
49/59
HsColour: Bug 1
data Prefs = ... deriving (Read,Show)z Uses read/show serialisation to a file
z readFile prefs, then read result
z Potential crash if the user has modified thefile
z Real crash when Prefs structure changed!
FIXED
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
50/59
HsColour: Bug 1 Catch
> Catch HsColour.hsCheck Prelude.read: no parse
Partial Prelude.read$252Partial Language.Haskell.HsColour
.Colourise.parseColourPrefs
...
Partial Main.mainFull log is recordedAll preconditions
and properties
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
51/59
HsColour: Bug 2
zThe latex output mode had:outToken (\:xs) = ++ init xs ++
z file.hs:
z hscolour latex file.hs
z Crash
FIXED
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
52/59
HsColour: Bug 3
zThe html anchor output mode had:outToken ( :xs) = ++ init xs ++
z file.hs: ( )
z hscolour html anchor file.hs
z Crash
F
IXED
CH
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
53/59
HsColour: Problem 4
z A pattern match without a [] casez A nice refactoring, but not a crash
z
Proof was complex, distributed and fragile Based on the length of comment lexemes!
z End result: HsColour cannot crash Or could not at the date I checked it...
z Required 2.1 seconds, 2.7Mb
CHANGED
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
54/59
Case Study: FiniteMap library
z Over 10 years old, was a standard libraryz 14 non-exhaustive patterns, 13 are safe
delFromFM (Branch key ..) del_key
| del_key > key = ...
| del_key < key = ...
| del_key key = ...
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
55/59
Case Study: XMonad
z Haskell Window Managerz Central module (StackSet)
z
Checked by Catch as a library
z No bugs, but suggested refactorings
z Made explicit some assumptions about Num
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
56/59
Catchs Failings
z Weakest Area: Yhc Conversion from Haskell to Core requires Yhc
Can easily move to using GHC Core (once fixed)
z 2nd Weakest Area: First-order transform
Still working on this Could use supercompilation
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
57/59
??-Constraints
z Could solve more complex problemsz Could retain numeric constraints precisely
z
Ideally have a single normal form
z MP-constraints work well, but there is roomfor improvement
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
58/59
Alternatives to Catch
z Reach, SmallCheck Matt Naylor, Colin R Enumerative testing to some depth
z
ESC/Haskell - Dana Xu Precondition/postcondition checking
z Dependent types Epigram, Cayenne
Push conditions into the types
-
7/29/2019 Slides-Detecting Pattern Match Failures in Haskell-26 Nov 2007
59/59
Conclusions
z Pattern matching is an important area thathas been overlooked
z Framework separate from constraints Can replace constraints for different power
z Catch is a good step towards the solution
Practical tool Has found real bugs
www.cs.york.ac.uk/~ndm/catch