# Sound Haskell Dana N. Xu University of Cambridge Joint work with Simon Peyton Jones Microsoft Research Cambridge Koen Claessen Chalmers University of Technology.

Post on 04-Jan-2016

212 views

TRANSCRIPT

Sound HaskellDana N. XuUniversity of CambridgeJoint work with

Simon Peyton JonesMicrosoft Research CambridgeKoen ClaessenChalmers University of Technology

Module UserPgm where

f :: [Int]->Intf xs = head xs `max` 0

: f [] Program Errors Give Headache!Glasgow Haskell Compiler (GHC) gives at run-time

Exception: Prelude.head: empty listModule Prelude where

head :: [a] -> ahead (x:xs) = xhead [] = error empty list

Types>> > > > > Contracts >> > > > > head (x:xs) = x

head :: [Int] -> Int

(head 1)

head :: {x | not (null xs)} -> {r | True}

(head [])Bug!Bug!Contract(original Haskell boolean expression)Typenot :: Bool -> Boolnot True = Falsenot False = True

null :: [a] -> Boolnull [] = Truenull (x:xs) = False

Preconditionshead :: {xs | not (null xs)} -> {r | True}head (x:xs) = 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` 0No more warnings from compiler!

Expressiveness of the Specification Languagedata T = T1 Bool | T2 Int | T3 T T

sumT :: T -> IntsumT :: {x | noT1 x} -> {r | True}sumT (T2 a) = asumT (T3 t1 t2) = sumT t1 + sumT t2

noT1 :: T -> BoolnoT1 (T1 _) = FalsenoT1 (T2 _) = TruenoT1 (T3 t1 t2) = noT1 t1 && noT1 t2

Expressiveness of the Specification LanguagesumT :: T -> IntsumT :: {x | noT1 x} -> {r | True}sumT (T2 a) = asumT (T3 t1 t2) = sumT t1 + sumT t2

rmT1 :: T -> TrmT1 :: {x | True} -> {r | noT1 r}rmT1 (T1 a) = if a then T2 1 else T2 0rmT1 (T2 a) = T2 armT1 (T3 t1 t2) = T3 (rmT1 t1) (rmT1 t2)

For all crash-free t::T, sumT (rmT1 t) will not crash.

Functions without Annotationsdata T = T1 Bool | T2 Int | T3 T T

noT1 :: T -> BoolnoT1 (T1 _) = FalsenoT1 (T2 _) = TruenoT1 (T3 t1 t2) = noT1 t1 && noT1 t2

(&&) True x = x(&&) False x = FalseNo abstraction is more compact than the function definition itself!

Higher Order Functionsall :: (a -> Bool) -> [a] -> Boolall f [] = Trueall f (x:xs) = f x && all f xs

filter :: (a -> Bool) -> [a] -> [a]filter :: {p | True} -> {xs | True} -> {r | all p r}filter p [] = []filter p (x:xs) = case (p x) of True -> x : filter p xs False -> filter p xs

Contracts for higher-order functions parameterf1 :: (Int -> Int) -> Intf1 :: ({x | True} -> {y | y >= 0}) -> {r | r >= 0}f1 g = (g 1) - 1

f2:: {r | True}f2 = f1 (\x -> x 1)Error: f1s postcondition fails because (g 1) >= 0 does not imply (g 1) 1 >= 0Error: f2 calls f1 which fails f1s precondition

Lazinessfst (a,b) = a

Option 1 ()fst :: {x | True} -> {r | True}

Option 2 ()fst :: ({x | True}, Any) -> {r | True} fst (5, error f)

fstN :: (Int, Int) -> IntfstN :: ({x | True}, Any) -> {r | True}fstN (a, b) n = if n > 0 then fstN (a+1, b) (n-1) else a

g2 = fstN (5, error fstN) 100Every expression satisfies Any

Various Exampleszip :: [a] -> [b] -> [(a,b)]zip :: {xs | True} -> {ys | sameLen xs ys} -> {rs | sameLen rs xs }

sameLen [] [] = TruesameLen (x:xs) (y:ys) = sameLen xs yssameLen _ _ = False

f91 :: Int -> Intf91 :: { n {r | r == 91 }f91 n = case (n f91 (f91 (n + 11)) False -> n 10

Sortingsorted [] = Truesorted (x:[]) = Truesorted (x:y:xs) = x {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}(==>) True x = x(==>) False x = True

Contract Synonymtype Ok = {x | True}type NonNull = {x | not (null x)}

head :: [Int] -> Inthead :: NonNull -> Okhead (x:xs) = x

{-# type Ok = {x | True} -#}{-# type NonNull = {x | not (null x)} #-}{-# contract head :: NonNull -> Ok #-}Actual Syntax

What we cant dog1, g2 :: Ok -> Okg1 x = case (prime x > square x) of True -> x False -> error urk

g2 xs ys = case (rev (xs ++ ys) == rev ys ++ rev xs) of True -> xs False -> error urk

Hence, three possible outcomes: (1) Definitely Safe (no crash, but may loop)(2) Definite Bug (definitely crashes)(3) Possible BugCrash!Crash!

LanguageSyntaxfollowing Haskellslazy semantics

Two special constructorsBAD is an expression that crashes.error :: String -> aerror s = BAD

head (x:xs) = xhead [] = 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)

CrashingDefinition (Crash). A closed term e crashes iff e !* BAD

Definition (Crash-free Expression)An expression e is crash-free iff8 C. BAD 2 C, ` C[[e]] :: (), C[[e]] !* BAD

What 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 checkNo pattern matching failurePrecondition of all calls in the body of f holdsPostcondition holds for f itself.

How to Check?Given e and tWe 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 1eBt is crash-free , e 2 t

Theorem 2simpl (eBt) is syntactically safe ) eBt is crash-free This TalkESC/Haskell (HW06)

Syntax of ContractsFull version: x:{x | x >0} -> {r | r > x}Short hand: {x | x > 0} -> {r | r > x}

k:({x | x > 0} -> {y | y > 0}) -> {r | r > k 5}

Contract Satisfactione" means e diverges or e !* UNR

B pronounced ensuresC pronounced requirese B {x | p} = case p[e/x] ofTrue -> eFalse -> BAD

e C {x | p} = case p[e/x] ofTrue -> eFalse -> UNRExample:5 2 {x | x > 0} 5 B {x | x > 0}= case (5 > 0) of True -> 5 False -> BAD

Function Contract and Tuple Contracte B x:t1 ! t2 = v. (e (vC t1)) B t2[vCt1/x]

e C x:t1 ! t2 = v. (e (vB t1)) C t2[vBt1/x]

e B (t1, t2) = case e of (e1, e2) -> (e1 B t1, e2 B t2)

e C (t1, t2) = case e of (e1, e2) -> (e1 C t1, e2 C t2)

f :: {x | x > 0} -> {r | True}f x = x

f B {x | x > 0} -> {r | True}=(x.x) B {x | x > 0} -> {r | True}= v. (x.x (v C {x | x > 0})) B {r | True}= v. (case v > 0 of True -> v False -> UNR)

g = ff C {x | x > 0} -> {r | True}= v. (case v > 0 of True -> v False -> BAD)

Higher-Order Functionf1 B ({x | True} -> {y | y >= 0}) -> {r | r >= 0}= B C B= v1. case (v1 1) >= 0 of True -> case (v1 1) - 1 >= 0 of True -> (v1 1) -1 False -> BAD False -> UNR f1 :: (Int -> Int) -> Intf1 :: ({x | True} -> {y | y >= 0}) -> {r | r >= 0}f1 g = (g 1) - 1

f2:: {r | True}f2 = f1 (\x -> x 1)

Any Contracte B Any = UNRe C Any = BADf5 :: Any -> {r | True}f5 x = 5

error :: String -> a -- HM Typeerror :: {x | True} -> Any -- Contract

Properties of B and CLemma1: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 te C t v e

Definition (Crashes-More-Often):e1 v e2 iff for all C, ` C[[ei]] :: () for i=1,2 and(1) C[[e2]] !* BAD ) C[[e1]] !* BAD

About 30 Lemmas Lemma [Monotonicity of Satisfaction ]:If e1 2 t and e1 v e2, then e22 tLemma [Congruence of v]:e1 v e2 ) 8 C. C[[e1]] v C[[e2]]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 eLemma [A Closure Pair]:8 e, t. e v eC t B t

ContributionsAutomatic static contract checking instead of dynamic contract checking.Compared with ESC/HaskellAllow 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 GHCAccept full HaskellSupport separate compilation as the verification is modular.Can check functions without contract through CEG unrolling.

**********

Recommended