From Types to Contracts head [] = BAD head (x:xs) = x head :: [a] -> a (head 1) Bug! Type BAD means should not happen: crash null :: [a] - > Bool null [] = True null (x:xs) = False head {xs not (null xs)} -> {r True} (head []) Bug! Contract (arbitrary Haskell boolean expression)
What we want Adapt Findler- Felleisen s ideas for dynamic (high- order) contract checking. Do stahc contract checking for a lazy language. Contract Haskell function Glasgow Haskell Compiler (GHC) Where the bug is Why it is a bug
Three Outcomes (1) Definitely Safe (no crash, but may loop) (2) Definite Bug (definitely crashes) (3) Possible Bug
SorHng (==>) True x = x (==>) False x = True sorted [] = True sorted (x:[]) = True sorted (x:y:xs) = x <= y && sorted (y : xs) insert :: Int -> [Int] -> [Int] insert {i True} -> {xs sorted xs} -> {r sorted r} merge :: [Int] -> [Int] -> [Int] merge {xs sorted xs}->{ys sorted ys}->{r sorted r} bubblehelper :: [Int] -> ([Int], Bool) bubblehelper {xs True} -> {r not (snd r) ==> sorted (fst r)} insertsort, mergesort, bubblesort {xs True} -> {r sorted r} 5
AVL Tree (&&) True x = x (&&) False x = False balanced :: AVL -> Bool balanced L = True balanced (N t u) = balanced t && balanced u && abs (depth t - depth u) <= 1 data AVL = L N Int AVL AVL insert, delete :: AVL -> Int -> AVL insert {x balanced x} -> {y True} -> {r notleaf r && balanced r && 0 <= depth r - depth x && depth r - depth x <= 1 } delete {x balanced x} -> {y True} -> {r balanced r && 0 <= depth x - depth r && depth x - depth r <= 1}
The Contract Idea for Higher- Order FuncHon [Findler/Felleisen] f1 :: (Int -> Int) -> Int f1 ({x x > 0} -> {y y >= 0}) -> {r r >= 0} f1 g = (g 0) - 1 Blame f1: f1 calls g with wrong argument f1 does not sahsfy its post- condihon f2 :: {r True} f2 = f1 (\x -> x 5) f3 :: {r True} f3 = f1 (\x -> x 1) Can t tell at run-time Blame f2: f2 calls f1 with wrong argument f3 is Ok.
What is a Contract? Ok = {x True} arbitrary Haskell boolean expression Contract t ::= {x p} Predicate Contract x:t 1 -> t 2 Dependent Function Contract (t 1, t 2 ) Tuple Contract Any Polymorphic Any Contract 3 { x x > 0 } (3, []) Any 3 { x True } (3, []) (Ok, {ys null ys}) arbitrary constructor inc x:{x x>0} -> {y y == x + 1} Precondition Postcondition Postcondition can mention argument
What we want? Check f <contract_of_f> If main Ok, then the whole program cannot crash. If not, show which funchon to blame and why. Beauty of Contract Checking
Main Theorem e t iff e t is crash-free (related to Blume&McAllester:JFP 06) ESC/Haskell [Haskell 06] Symbolically simplify (e t) See if BAD is syntactically in e. If yes, DONE; else give BLAME [POPL 10] Define e t Construct e t (e ensures t) some e
Wrappers and ( pronounced ensures pronounced requires) e {x p} = case p[e/x] of True -> e False -> BAD e x:t 1 -> t 2 = λ v. (e (v t 1 )) t 2 [(v t 1 )/x] e (t 1, t 2 ) = case e of (e 1, e 2 ) -> (e 1 t 1, e 2 t 2 ) e Any = UNR related to [Findler-Felleisen:ICFP02]
Wrappers and ( pronounced ensures pronounced requires) e {x p} = case p[e/x] of True -> e False -> UNR e x:t 1 -> t 2 = λ v. (e (v t 1 )) t 2 [v t 1 /x] e (t 1, t 2 ) = case e of (e 1, e 2 ) -> (e 1 t 1, e 2 t 2 ) e Any = BAD related to [Findler-Felleisen:ICFP02]
Some InteresHng Details Theory Contracts that loop Contracts that crash Lovely Lemmas Practice Adding tags, e.g. BAD f Achieves precise blaming More tags to trace funchons to blame Achieves the same goal of [Meunier:POPL06] Using a theorem prover Counter- example guided unrolling
Lovely Lemmas
Summary StaHc contract checking is a ferhle and under- researched area DisHncHve features of our approach Full Haskell in contracts; absolutely crucial DeclaraHve specificahon of sahsfies Nice theory (with some very tricky corners) StaHc proofs Modular Checking Compiler as theorem prover
Afer Ph.D. Postdoc project in 2009: probabilishc contract for component base design [ATVA 2010] Current project at Xavier Leroy s team (INRIA) - a verifying compiler: 1. Apply the idea to OCaml compiler by allowing both stahc and dynamic contract checking 2. Connect with Coq to verify more programs stahcally. 3. Use Coq to prove the correctness of the framework. 4. Apply new ideas back to Haskell (e.g. GHC).
StaHc and Dynamic Static checking Program with Specifications Dynamic checking Compile Hme error amributes blame to the right place Run Hme error amributes blame to the right place No blaming means Program cannot crashs Or, more plausibly: If you guarantee that f t, then the program cannot crash
When does e sahsfy a contract? Brief, declarahve inc x:{x x > 0} -> {y y == x + 1} Precondition Postcondition Postcondition can mention argument
When does e sahsfy a contract? e is crash- free iff C. BAD s C. C[e] * BAD
Crash- free Examples Crash- free? \x -> x YES (1, True) YES (1, BAD) NO \x -> if x > 0 then x else (BAD, x) NO \x -> if x*x >= 0 then x + 1 else BAD Hmm.. YES Lemma: e is syntachcally safe => e is crash- free.
When does e sahsfy a contract? See the paper for Why e must be crash- free to sahsfy predicate contract? Why divergent expression sahsfies all contract? What if contract diverges (i.e. p diverges)? What if contract crashes (i.e. p crashes)?
head:: [a] -> a head [] = BAD head (x:xs) = x Example head { xs not (null xs) } -> Ok head {xs not (null xs)} -> Ok = \v. head (v {xs not (null xs)}) Ok e Ok = e = \v. head (v {xs not (null xs)}) = \v. head (case not (null v) of True -> v False -> UNR)
\v. head (case not (null v) of True -> v False -> UNR) Now inline not and null = \v. head (case v of [] -> UNR (p:ps) -> p) null :: [a] -> Bool null [] = True null (x:xs) = False not :: Bool -> Bool not True = False not False = True Now inline head = \v. case v of [] -> UNR (p:ps) -> p head:: [a] -> a head [] = BAD head (x:xs) = x So head [] fails with UNR, not BAD, blaming the caller
[Flanaghan, Mitchell, Pottier] Static checking StaHc and Dynamic Compile Hme error amributes blame to the right place Program with Specifications Dynamic checking Run Hme error amributes blame to the right place [Findler, Felleisen, Blume, Hinze, Loh, Runciman, Chitil]