> module Assert (die, fixme, lAssert, divide) where
>
> divide :: Double -> Int -> Double
> {-@ LIQUID "--short-names" @-}
> {-@ LIQUID "--no-termination" @-}As a warm up, first lets recall how we can specify and verify assertions with Refinement Types.
Recall from lecture, that we can define a function that should never be called.
> {-@ die :: {v:String | false} -> a @-}
> die str = error ("Oops, I died!" ++ str)We can use this function in all those places where we need to handle some case that we can know (and want to prove) will never happen at run-time.
Here’s a variant ofdie that we will use for those places where you need to fill in code:
> {-@ fixme :: {v:String | false} -> a @-}
> fixme str = error ("Oops, you didn't fill in the code for: "++ str)Lets define a refined type for the Bool that is always true:
> {-@ type TRUE = {v:Bool | Prop v} @-}Notice that we can now assign the type TRUE to any Bool expression that is guaranteed to evaluate to True:
> {-@ one_plus_one_eq_two :: TRUE @-}
> one_plus_one_eq_two = (1 + 1 == 2)But of course, not to expressions that are False:
> {-@ one_plus_one_eq_three :: TRUE @-}
> one_plus_one_eq_three = (1 + 1 == 3) -- TYPE ERRORWe can now use TRUE to define a lAssert function:
> {-@ lAssert :: TRUE -> TRUE @-}
> lAssert True = True
> lAssert False = die "Assert Fails!"Now, we can call lAssert only on expressions that we want to prove always hold, for example:
> propOk = lAssert (1 + 1 == 2)But if we try to lAssert bogus facts then they are rejected:
> propFail = lAssert (1 + 1 == 3) -- TYPE ERRORFinally, lets write a safe divide by zero operator (that we will use later)
> {-@ divide :: Double -> {v:Int | v /= 0}-> Double @-}
> divide n 0 = die "oops divide by zero"
> divide n d = n / (fromIntegral d)