Title: Sound Haskell
1Sound Haskell
- Dana N. Xu
- University of Cambridge
- Joint work with
Simon Peyton Jones Microsoft Research Cambridge
Koen Claessen Chalmers University of Technology
2Program Errors Give Headache!
- Module UserPgm where
- f Int-gtInt
- f xs head xs max 0
-
- f
Module Prelude where head a -gt a head
(xxs) x head error empty list
Glasgow Haskell Compiler (GHC) gives at
run-time Exception Prelude.head empty list
3Typesgtgt gt gt gt gt Contracts gtgt gt gt gt gt
- head (xxs) x
- head Int -gt Int
- (head 1)
- head x not (null xs)
- -gt r True
- (head )
Type
not Bool -gt Bool not True False not False
True null a -gt Bool null True null
(xxs) False
Bug!
Contract (original Haskell boolean expression)
Bug!
4Preconditions
- head xs not (null xs) -gt r True
- head (xxs) x
- f xs head xs max 0
- Warning f calls head
- which may fail heads precondition!
- f_ok xs if null xs then 0
- else head xs max 0
No more warnings from compiler!
5Expressiveness of the Specification Language
data T T1 Bool T2 Int T3 T T sumT T -gt
Int sumT x noT1 x -gt r True sumT (T2
a) a sumT (T3 t1 t2) sumT t1 sumT t2 noT1
T -gt Bool noT1 (T1 _) False noT1 (T2 _)
True noT1 (T3 t1 t2) noT1 t1 noT1 t2
6Expressiveness of the Specification Language
- sumT T -gt Int
- sumT x noT1 x -gt r True
- sumT (T2 a) a
- sumT (T3 t1 t2) sumT t1 sumT t2
- rmT1 T -gt T
- rmT1 x True -gt r noT1 r
- rmT1 (T1 a) if a then T2 1 else T2 0
- rmT1 (T2 a) T2 a
- rmT1 (T3 t1 t2) T3 (rmT1 t1) (rmT1 t2)
- For all crash-free tT, sumT (rmT1 t) will not
crash.
7Functions without Annotations
- data T T1 Bool T2 Int T3 T T
- noT1 T -gt Bool
- noT1 (T1 _) False
- noT1 (T2 _) True
- noT1 (T3 t1 t2) noT1 t1 noT1 t2
- () True x x
- () False x False
No abstraction is more compact than the function
definition itself!
8Higher Order Functions
- all (a -gt Bool) -gt a -gt Bool
- all f True
- all f (xxs) f x all f xs
- filter (a -gt Bool) -gt a -gt a
- filter p True -gt xs True -gt r all p
r - filter p
- filter p (xxs) case (p x) of
- True -gt x filter p xs
- False -gt filter p xs
9Contracts for higher-order functions parameter
- f1 (Int -gt Int) -gt Int
- f1 (x True -gt y y gt 0) -gt r r gt
0 - f1 g (g 1) - 1
- f2 r True
- f2 f1 (\x -gt x 1)
Error f1s postcondition fails because (g
1) gt 0 does not imply (g 1) 1
gt 0 Error f2 calls f1 which fails f1s
precondition
10Laziness
- fst (a,b) a
- Option 1 (?)
- fst x True -gt r True
- Option 2 (?)
- fst (x True, Any) -gt r True
-
- fst (5, error f)
- fstN (Int, Int) -gt Int
- fstN (x True, Any) -gt r True
- fstN (a, b) n if n gt 0
- then fstN (a1, b) (n-1)
- else a
- g2 fstN (5, error fstN) 100
Every expression satisfies Any
11Various Examples
- zip a -gt b -gt (a,b)
- zip xs True -gt ys sameLen xs ys
- -gt rs sameLen rs xs
- sameLen True
- sameLen (xxs) (yys) sameLen xs ys
- sameLen _ _ False
- f91 Int -gt Int
- f91 n lt 101 -gt r r 91
- f91 n case (n lt 100) of
- True -gt f91 (f91 (n 11))
- False -gt n 10
12Sorting
(gt) True x x (gt) False x True
- sorted True
- sorted (x) True
- sorted (xyxs) x lt y sorted (y xs)
- insert i True -gt xs sorted xs
- -gt r sorted r
- merge Int -gt Int -gt Int
- merge xs sorted xs -gt ys sorted ys
- -gt r sorted r
- bubbleHelper Int -gt (Int, Bool)
- bubbleHelper xs True
- -gt r not (snd r) gt sorted (fst
r) - Insertsort, mergesort, bubblesort xs True
- -gt r sorted
r
13Contract Synonym
- type Ok x True
- type NonNull x not (null x)
- head Int -gt Int
- head NonNull -gt Ok
- head (xxs) x
- - type Ok x True -
- - type NonNull x not (null x) -
- - contract head NonNull -gt Ok -
Actual Syntax
14What we cant do
- g1, g2 Ok -gt Ok
- g1 x case (prime x gt square x) of
- True -gt x
- False -gt error urk
- g2 xs ys
- case (rev (xs ys) rev ys rev xs) of
- True -gt xs
- False -gt error urk
Crash!
Crash!
Hence, three possible outcomes (1) Definitely
Safe (no crash, but may loop) (2) Definite Bug
(definitely crashes) (3) Possible Bug
15LanguageSyntax
following Haskells lazy semantics
16Two special constructors
- BAD is an expression that crashes.
- error String -gt a
- error s BAD
- head (xxs) x
- head BAD
- UNR (short for unreachable) is an expression
that gets stuck. This is not a crash, although
execution comes to a halt without delivering a
result. (identifiable infinite loop)
17Crashing
- Definition (Crash).
- A closed term e crashes iff e ! BAD
- Definition (Crash-free Expression)
- An expression e is crash-free iff
- 8 C. BAD 2 C, Ce (), Ce ! BAD
18What to Check?
- Does function f satisfies its contract t (written
f2 t)? - Goal main 2 x True
- At the definition of each function f,
- assuming the given precondition holds,
- we check
- No pattern matching failure
- Precondition of all calls in the body of f holds
- Postcondition holds for f itself.
19How to Check?
This Talk
- Given e and t
- We construct a term (eBt) (pronounced e ensures
t) - Prove (eBt) is crash-free.
- simplify the term (eBt) to e and e is
syntactically safe, then we are done. - Theorem 1
- eBt is crash-free , e 2 t
- Theorem 2
- simpl (eBt) is syntactically safe ) eBt is
crash-free
ESC/Haskell (HW06)
20Syntax of Contracts
Full version xx x gt0 -gt r r gt
x Short hand x x gt 0 -gt r r gt
x k(x x gt 0 -gt y y gt 0) -gt r r
gt k 5
21Contract Satisfaction
e" means e diverges or e ! UNR
22B pronounced ensuresC pronounced requires
- e B x p
- case pe/x of
- True -gt e
- False -gt BAD
- e C x p
- case pe/x of True -gt e
- False -gt UNR
- Example
- 5 2 x x gt 0
-
- 5 B x x gt 0
- case (5 gt 0) of
- True -gt 5
- False -gt BAD
23Function Contract and Tuple Contract
- e B xt1 ! t2
- ? v. (e (vC t1)) B t2vCt1/x
- e C xt1 ! t2
- ? v. (e (vB t1)) C t2vBt1/x
- e B (t1, t2)
- case e of
- (e1, e2) -gt (e1 B t1, e2 B t2)
- e C (t1, t2)
- case e of
- (e1, e2) -gt (e1 C t1, e2 C t2)
24- f x x gt 0 -gt r True
- f x x
- f B x x gt 0 -gt r True
- (?x.x) B x x gt 0 -gt r True
- ?v. (?x.x (v C x x gt 0)) B r True
- ?v. (case v gt 0 of
- True -gt v
- False -gt UNR)
- g f
f C x x gt 0 -gt r True ?v. (case v gt 0
of True -gt v False -gt BAD)
25Higher-Order Function
f1 (Int -gt Int) -gt Int f1 (x True -gt y
y gt 0) -gt r r gt 0 f1 g (g 1) - 1 f2
r True f2 f1 (\x -gt x 1)
- f1 B (x True -gt y y gt 0) -gt r r gt
0 - B C
B - ? v1. case (v1 1) gt 0 of
- True -gt case (v1 1) - 1 gt 0 of
- True -gt (v1 1) -1
- False -gt BAD
- False -gt UNR
26Any Contract
e B Any UNR e C Any BAD
f5 Any -gt r True f5 x 5 error String
-gt a -- HM Type error x True -gt
Any -- Contract
27(No Transcript)
28Properties of B and C
- Lemma1
- For all closed, crash-free e, and closed t,
- e C t 2 t
- Lemma2
- For all e and t, if e2 t, then
- e v e B t
- e C t v e
- Definition (Crashes-More-Often)
- e1 v e2 iff for all C, Cei () for
i1,2 and - (1) Ce2 ! BAD ) Ce1 ! BAD
29About 30 Lemmas ?
- Lemma Monotonicity of Satisfaction
- If e1 2 t and e1 v e2, then e22 t
- Lemma Congruence of v
- e1 v e2 ) 8 C. Ce1 v Ce2
- Lemma Idempotence of Projection
- 8 e, t. e B t B t e B t
- 8 e, t. e C t C t eC t
- Lemma A Projection Pair
- 8 e, t. e B t C t v e
- Lemma A Closure Pair
- 8 e, t. e v eC t B t
30Contributions
- Automatic static contract checking instead of
dynamic contract checking. - Compared with ESC/Haskell
- Allow pre/post specification for higher-order
functions parameter through contracts. - Reduce more false alarms caused by Laziness and
in an efficient way. - Allow user-defined data constructors to be used
to define contracts - Specifications are more type-like, so we can
define contract synonym. - We develop a concise notation (B and C) for
contract checking, which enjoys many properties.
We give a new and relatively simpler proof of the
soundness and completeness of dynamic contract
checking, this proof is much trickier than it
looks. - We implement the idea in GHC
- Accept full Haskell
- Support separate compilation as the verification
is modular. - Can check functions without contract through CEG
unrolling.