--- layout: post title: "Lets Talk About Sets" date: 2013-01-05 16:12 comments: true external-url: categories: basic, measures, sets author: Ranjit Jhala published: false --- In the posts so far, we've seen how LiquidHaskell allows you to use SMT solvers to specify and verify *numeric* invariants -- denominators that are non-zero, integer indices that are within the range of an array, vectors that have the right number of dimensions and so on. However, SMT solvers are not limited to numbers, and in fact, support rather expressive logics. In the next couple of posts, lets see how LiquidHaskell uses SMT to talk about **sets of values**, for example, the contents of a list, and to specify and verify properties about those sets.
24: module ListSets whereTalking about Sets (In Logic) ============================= First, we need a way to talk about sets in the refinement logic. We could roll our own special Haskell type, but why not just use the `Set a` type from `Data.Set`.
35: import Data.Set hiding (filter, split) 36: import Prelude hiding (reverse, filter)The above, also instructs LiquidHaskell to import in the various specifications defined for the `Data.Set` module that we have *predefined* in [Data/Set.spec][setspec] Lets look at the specifications. The `embed` directive tells LiquidHaskell to model the **Haskell**
46: type constructor `Set` with the **SMT** type constructor `Set_Set`. 47: 48: module spec Data.Set where 49: 50: embed Set as Set_SetFirst, we define the logical operators (i.e. `measure`s)
54: measure Set_sng :: a -> (Set a) -- ^ singleton 55: measure Set_cup :: (Set a) -> (Set a) -> (Set a) -- ^ union 56: measure Set_cap :: (Set a) -> (Set a) -> (Set a) -- ^ intersection 57: measure Set_dif :: (Set a) -> (Set a) -> (Set a) -- ^ differenceNext, we define predicates on `Set`s
61: measure Set_emp :: (Set a) -> Prop -- ^ emptiness 62: measure Set_mem :: a -> (Set a) -> Prop -- ^ membership 63: measure Set_sub :: (Set a) -> (Set a) -> Prop -- ^ inclusionInterpreted Operations ---------------------- The above operators are *interpreted* by the SMT solver. That is, just like the SMT solver *"knows that"* 2 + 2 == 4 the SMT solver *"knows that"* (Set_sng 1) == (Set_cap (Set_sng 1) (Set_cup (Set_sng 2) (Set_sng 1))) This is because, the above formulas belong to a decidable Theory of Sets which can be reduced to McCarthy's more general [Theory of Arrays][mccarthy]. See [this recent paper][z3cal] if you want to learn more about how modern SMT solvers *"know"* the above equality holds... Talking about Sets (In Code) ============================ Of course, the above operators exist purely in the realm of the refinement logic, beyond the grasp of the programmer. To bring them down (or up, or left or right) within reach of Haskell code, we can simply *assume* that the various public functions in `Data.Set` do the *Right Thing* i.e. produce values that reflect the logical set operations: First, the functions that create `Set` values
95: empty :: {v:(Set a) | (Set_emp v)} 96: singleton :: x:a -> {v:(Set a) | v = (Set_sng x)}Next, the functions that operate on elements and `Set` values
100: insert :: Ord a => x:a -> xs:(Set a) -> {v:(Set a) | v = (Set_cup xs (Set_sng x))} 101: delete :: Ord a => x:a -> xs:(Set a) -> {v:(Set a) | v = (Set_dif xs (Set_sng x))}Then, the binary `Set` operators
105: union :: Ord a => xs:(Set a) -> ys:(Set a) -> {v:(Set a) | v = (Set_cup xs ys)} 106: intersection :: Ord a => xs:(Set a) -> ys:(Set a) -> {v:(Set a) | v = (Set_cap xs ys)} 107: difference :: Ord a => xs:(Set a) -> ys:(Set a) -> {v:(Set a) | v = (Set_dif xs ys)}And finally, the predicates on `Set` values:
111: isSubsetOf :: Ord a => xs:(Set a) -> ys:(Set a) -> {v:Bool | (Prop v) <=> (Set_sub xs ys)} 112: member :: Ord a => x:a -> xs:(Set a) -> {v:Bool | (Prop v) <=> (Set_mem x xs)}**Note:** Oh quite. We shouldn't and needn't really *assume*, but should and will *guarantee* that the functions from `Data.Set` implement the above types. But thats a story for another day... Proving Theorems With LiquidHaskell =================================== OK, lets take our refined operators from `Data.Set` out for a spin! One pleasant consequence of being able to precisely type the operators from `Data.Set` is that we have a pleasant interface for using the SMT solver to *prove theorems* about sets, while remaining firmly rooted in Haskell. First, lets write a simple function that asserts that its input is `True`
131: {-@ boolAssert :: {v: Bool | (Prop v)} -> {v:Bool | (Prop v)} @-} 132: {VV : (GHC.Types.Bool) | (? Prop([VV]))} -> {VV : (GHC.Types.Bool) | (? Prop([VV]))}boolAssert True = {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV = True)}True 133: boolAssert False = [(GHC.Types.Char)] -> {VV : (GHC.Types.Bool) | false}error {VV : [(GHC.Types.Char)] | (len([VV]) >= 0)}"boolAssert: False? Never!"Now, we can use `boolAssert` to write some simple properties. (Yes, these do indeed look like QuickCheck properties -- but here, instead of generating tests and executing the code, the type system is proving to us that the properties will *always* evaluate to `True`) Lets check that `intersection` is commutative ...
144: {-@ prop_cap_comm :: Set Int -> Set Int -> Bool @-} 145: prop_cap_comm :: Set Int -> Set Int -> Bool 146: (Data.Set.Base.Set (GHC.Types.Int)) -> (Data.Set.Base.Set (GHC.Types.Int)) -> (GHC.Types.Bool)prop_cap_comm (Data.Set.Base.Set (GHC.Types.Int))x (Data.Set.Base.Set (GHC.Types.Int))y 147: = {VV : (GHC.Types.Bool) | (? Prop([VV]))} -> {VV : (GHC.Types.Bool) | (? Prop([VV]))}boolAssert 148: ({VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)} -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)}) -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)} -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)}$ (GHC.Types.Bool)({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = x)}x xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cap([xs; ys]))}`intersection` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = y)}y) GHC.Classes.Eq (Data.Set.Base.Set GHC.Types.Int)== ({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = y)}y xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cap([xs; ys]))}`intersection` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = x)}x)that `union` is associative ...
154: {-@ prop_cup_assoc :: Set Int -> Set Int -> Set Int -> Bool @-} 155: prop_cup_assoc :: Set Int -> Set Int -> Set Int -> Bool 156: (Data.Set.Base.Set (GHC.Types.Int)) -> (Data.Set.Base.Set (GHC.Types.Int)) -> (Data.Set.Base.Set (GHC.Types.Int)) -> (GHC.Types.Bool)prop_cup_assoc (Data.Set.Base.Set (GHC.Types.Int))x (Data.Set.Base.Set (GHC.Types.Int))y (Data.Set.Base.Set (GHC.Types.Int))z 157: = {VV : (GHC.Types.Bool) | (? Prop([VV]))} -> {VV : (GHC.Types.Bool) | (? Prop([VV]))}boolAssert 158: ({VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)} -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)}) -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)} -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)}$ (GHC.Types.Bool)({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = x)}x xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cup([xs; ys]))}`union` ({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = y)}y xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cup([xs; ys]))}`union` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = z)}z)) x:(Data.Set.Base.Set (GHC.Types.Int)) -> y:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (GHC.Types.Bool) | ((? Prop([VV])) <=> (x = y))}== (Data.Set.Base.Set (GHC.Types.Int))({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = x)}x xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cup([xs; ys]))}`union` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = y)}y) xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cup([xs; ys]))}`union` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = z)}zand that `union` distributes over `intersection`.
164: {-@ prop_cap_dist :: Set Int -> Set Int -> Set Int -> Bool @-} 165: prop_cap_dist :: Set Int -> Set Int -> Set Int -> Bool 166: (Data.Set.Base.Set (GHC.Types.Int)) -> (Data.Set.Base.Set (GHC.Types.Int)) -> (Data.Set.Base.Set (GHC.Types.Int)) -> (GHC.Types.Bool)prop_cap_dist (Data.Set.Base.Set (GHC.Types.Int))x (Data.Set.Base.Set (GHC.Types.Int))y (Data.Set.Base.Set (GHC.Types.Int))z 167: = {VV : (GHC.Types.Bool) | (? Prop([VV]))} -> {VV : (GHC.Types.Bool) | (? Prop([VV]))}boolAssert 168: ({VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)} -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)}) -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)} -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)}$ ({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = x)}x xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cap([xs; ys]))}`intersection` ({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = y)}y xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cup([xs; ys]))}`union` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = z)}z)) 169: x:(Data.Set.Base.Set (GHC.Types.Int)) -> y:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (GHC.Types.Bool) | ((? Prop([VV])) <=> (x = y))}== (Data.Set.Base.Set (GHC.Types.Int))({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = x)}x xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cap([xs; ys]))}`intersection` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = y)}y) xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cup([xs; ys]))}`union` ({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = x)}x xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cap([xs; ys]))}`intersection` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = z)}z)Of course, while we're at it, lets make sure LiquidHaskell doesn't prove anything thats *not* true ...
176: {-@ prop_cup_dif_bad :: Set Int -> Set Int -> Bool @-} 177: prop_cup_dif_bad :: Set Int -> Set Int -> Bool 178: (Data.Set.Base.Set (GHC.Types.Int)) -> (Data.Set.Base.Set (GHC.Types.Int)) -> (GHC.Types.Bool)prop_cup_dif_bad (Data.Set.Base.Set (GHC.Types.Int))x (Data.Set.Base.Set (GHC.Types.Int))y 179: = {VV : (GHC.Types.Bool) | (? Prop([VV]))} -> {VV : (GHC.Types.Bool) | (? Prop([VV]))}boolAssert 180: ((GHC.Types.Bool) -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)}) -> (GHC.Types.Bool) -> {VV : (GHC.Types.Bool) | (? Prop([VV])),(VV != False)}$ {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = x)}x x:(Data.Set.Base.Set (GHC.Types.Int)) -> y:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (GHC.Types.Bool) | ((? Prop([VV])) <=> (x = y))}== (Data.Set.Base.Set (GHC.Types.Int))({VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = x)}x xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_cup([xs; ys]))}`union` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = y)}y) xs:(Data.Set.Base.Set (GHC.Types.Int)) -> ys:(Data.Set.Base.Set (GHC.Types.Int)) -> {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = Set_dif([xs; ys]))}`difference` {VV : (Data.Set.Base.Set (GHC.Types.Int)) | (VV = y)}yHmm. You do know why the above doesn't hold, right? It would be nice to get a *counterexample* wouldn't it. Well, for the moment, there is QuickCheck! Thus, the refined types offer a nice interface for interacting with the SMT solver in order to prove theorems in LiquidHaskell. (BTW, The [SBV project][sbv] describes another approach for using SMT solvers from Haskell, without the indirection of refinement types.) While the above is a nice warm up exercise to understanding how LiquidHaskell reasons about sets, our overall goal is not to prove theorems about set operators, but instead to specify and verify properties of programs. The Set of Values in a List =========================== Lets see how we might reason about sets of values in regular Haskell programs. Lets begin with Lists, the jack-of-all-data-types. Now, instead of just talking about the **number of** elements in a list, we can write a measure that describes the **set of** elements in a list: A measure for the elements of a list, from [Data/Set.spec][setspec]
208: 209: measure listElts :: [a] -> (Set a) 210: listElts ([]) = {v | (? Set_emp(v))} 211: listElts (x:xs) = {v | v = (Set_cup (Set_sng x) (listElts xs)) }That is, `(elts xs)` describes the set of elements contained in a list `xs`. Next, to make the specifications concise, lets define a few predicate aliases:
219: {-@ predicate SameElts X Y = ((listElts X) = (listElts Y)) @-} 220: {-@ predicate SubElts X Y = (Set_sub (listElts X) (listElts Y)) @-} 221: {-@ predicate UnionElts X Y Z = ((listElts X) = (Set_cup (listElts Y) (listElts Z))) @-}A Trivial Identity ------------------ OK, now lets write some code to check that the `elts` measure is sensible!
230: {-@ listId :: xs:[a] -> {v:[a]| (SameElts v xs)} @-} 231: forall a. x1:[a] -> {VV : [a] | (len([VV]) = len([x1])), (listElts([VV]) = Set_cup([listElts([x1]); listElts([x1])])), (listElts([VV]) = listElts([x1])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (len([VV]) >= 0)}listId [] = forall <p :: a -> a -> Bool>. {VV : [{VV : a | false}]<p> | (? Set_emp([listElts([VV])])), (len([VV]) = 0)}[] 232: listId (x:xs) = {VV : a | (VV = x)}x forall <p :: a -> a -> Bool>. y:a -> ys:[a<p y>]<p> -> {VV : [a]<p> | (len([VV]) = (1 + len([ys]))), (listElts([VV]) = Set_cup([Set_sng([y]); listElts([ys])]))}: forall a. x1:[a] -> {VV : [a] | (len([VV]) = len([x1])), (listElts([VV]) = Set_cup([listElts([x1]); listElts([x1])])), (listElts([VV]) = listElts([x1])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (len([VV]) >= 0)}listId {VV : [a] | (VV = xs),(len([VV]) >= 0)}xsThat is, LiquidHaskell checks that the set of elements of the output list is the same as those in the input. A Less Trivial Identity ----------------------- Next, lets write a function to `reverse` a list. Of course, we'd like to verify that `reverse` doesn't leave any elements behind; that is that the output has the same set of values as the input list. This is somewhat more interesting because of the *tail recursive* helper `go`. Do you understand the type that is inferred for it? (Put your mouse over `go` to see the inferred type.)
249: {-@ reverse :: xs:[a] -> {v:[a]| (SameElts v xs)} @-} 250: forall a. xs:[a] -> {VV : [a] | (listElts([VV]) = listElts([xs]))}reverse = x1:{VV : [{VV : a | false}]<\_ VV -> false> | (len([VV]) = 0)} -> x2:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x1]); listElts([x2])])), (listElts([VV]) = Set_cup([listElts([x2]); listElts([x1])])), (len([VV]) >= 0)}go {VV : [{VV : a | false}]<\_ VV -> false> | (? Set_emp([listElts([VV])])), (len([VV]) = 0), (len([VV]) >= 0)}[] 251: where 252: acc:{VV : [a] | (len([VV]) >= 0)} -> x1:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([acc]); listElts([x1])])), (listElts([VV]) = Set_cup([listElts([x1]); listElts([acc])])), (len([VV]) >= 0)}go {VV : [a] | (len([VV]) >= 0)}acc [] = {VV : [a] | (VV = acc),(len([VV]) >= 0)}acc 253: go acc (y:ys) = acc:{VV : [a] | (len([VV]) >= 0)} -> x1:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([acc]); listElts([x1])])), (listElts([VV]) = Set_cup([listElts([x1]); listElts([acc])])), (len([VV]) >= 0)}go ({VV : a | (VV = y)}yforall <p :: a -> a -> Bool>. y:a -> ys:[a<p y>]<p> -> {VV : [a]<p> | (len([VV]) = (1 + len([ys]))), (listElts([VV]) = Set_cup([Set_sng([y]); listElts([ys])]))}:{VV : [a] | (VV = acc),(len([VV]) >= 0)}acc) {VV : [a] | (VV = ys),(len([VV]) >= 0)}ysAppending Lists --------------- Next, here's good old `append`, but now with a specification that states that the output indeed includes the elements from both the input lists.
263: {-@ append :: xs:[a] -> ys:[a] -> {v:[a] | (UnionElts v xs ys) } @-} 264: forall a. x1:[a] -> ys:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x1]); listElts([ys])])), (listElts([VV]) = Set_cup([listElts([ys]); listElts([x1])])), (len([VV]) >= 0)}append [] [a]ys = {VV : [a] | (VV = ys),(len([VV]) >= 0)}ys 265: append (x:xs) ys = {VV : a | (VV = x)}x forall <p :: a -> a -> Bool>. y:a -> ys:[a<p y>]<p> -> {VV : [a]<p> | (len([VV]) = (1 + len([ys]))), (listElts([VV]) = Set_cup([Set_sng([y]); listElts([ys])]))}: forall a. x1:[a] -> ys:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x1]); listElts([ys])])), (listElts([VV]) = Set_cup([listElts([ys]); listElts([x1])])), (len([VV]) >= 0)}append {VV : [a] | (VV = xs),(len([VV]) >= 0)}xs {VV : [a] | (VV = ys),(len([VV]) >= 0)}ysFiltering Lists --------------- Lets round off the list trilogy, with `filter`. Here, we can verify that it returns a **subset of** the values of the input list.
275: {-@ filter :: (a -> Bool) -> xs:[a] -> {v:[a]| (SubElts v xs)} @-} 276: 277: forall a. (a -> (GHC.Types.Bool)) -> x2:[a] -> {VV : [a] | (? Set_sub([listElts([VV]); listElts([x2])])), (listElts([x2]) = Set_cup([listElts([x2]); listElts([VV])])), (len([VV]) >= 0)}filter a -> (GHC.Types.Bool)f [] = forall <p :: a -> a -> Bool>. {VV : [{VV : a | false}]<p> | (? Set_emp([listElts([VV])])), (len([VV]) = 0)}[] 278: filter f (x:xs) 279: | a -> (GHC.Types.Bool)f {VV : a | (VV = x)}x = {VV : a | (VV = x)}x forall <p :: a -> a -> Bool>. y:a -> ys:[a<p y>]<p> -> {VV : [a]<p> | (len([VV]) = (1 + len([ys]))), (listElts([VV]) = Set_cup([Set_sng([y]); listElts([ys])]))}: forall a. (a -> (GHC.Types.Bool)) -> x2:[a] -> {VV : [a] | (? Set_sub([listElts([VV]); listElts([x2])])), (listElts([x2]) = Set_cup([listElts([x2]); listElts([VV])])), (len([VV]) >= 0)}filter a -> (GHC.Types.Bool)f {VV : [a] | (VV = xs),(len([VV]) >= 0)}xs 280: | otherwise = forall a. (a -> (GHC.Types.Bool)) -> x2:[a] -> {VV : [a] | (? Set_sub([listElts([VV]); listElts([x2])])), (listElts([x2]) = Set_cup([listElts([x2]); listElts([VV])])), (len([VV]) >= 0)}filter a -> (GHC.Types.Bool)f {VV : [a] | (VV = xs),(len([VV]) >= 0)}xsMerge Sort ========== Lets conclude this entry with one larger example: `mergeSort`. We'd like to verify that, well, the list that is returned contains the same set of elements as the input list. And so we will! But first, lets remind ourselves of how `mergeSort` works 1. `split` the input list into two halves, 2. `sort` each half, recursively, 3. `merge` the sorted halves to obtain the sorted list. Split ----- We can `split` a list into two, roughly equal parts like so:
305: forall a. x1:[a] -> ({VV : [a] | (? Set_sub([listElts([VV]); listElts([x1])])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (len([VV]) >= 0)} , {VV : [a] | (? Set_sub([listElts([VV]); listElts([x1])])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (len([VV]) >= 0)})<\x1 VV -> (? Set_sub([listElts([VV]); listElts([x1])])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (len([VV]) >= 0)>split [] = forall a b <p2 :: a -> b -> Bool>. x1:a -> b<p2 x1> -> (a , b)<p2>({VV : [{VV : a | false}]<\_ VV -> false> | (? Set_emp([listElts([VV])])), (len([VV]) = 0), (len([VV]) >= 0)}[], {VV : [{VV : a | false}]<\_ VV -> false> | (? Set_emp([listElts([VV])])), (len([VV]) = 0), (len([VV]) >= 0)}[]) 306: split (x:xs) = forall a b <p2 :: a -> b -> Bool>. x1:a -> b<p2 x1> -> (a , b)<p2>({VV : a | (VV = x)}xforall <p :: a -> a -> Bool>. y:a -> ys:[a<p y>]<p> -> {VV : [a]<p> | (len([VV]) = (1 + len([ys]))), (listElts([VV]) = Set_cup([Set_sng([y]); listElts([ys])]))}:{VV : [a] | (? Set_sub([listElts([VV]); listElts([xs])])), (VV = zs), (VV = zs), (len([VV]) = len([zs])), (listElts([VV]) = Set_cup([listElts([zs]); listElts([zs])])), (listElts([VV]) = listElts([zs])), (listElts([xs]) = Set_cup([listElts([xs]); listElts([VV])])), (listElts([xs]) = Set_cup([listElts([ys]); listElts([VV])])), (listElts([xs]) = Set_cup([listElts([ys]); listElts([VV])])), (listElts([zs]) = Set_cup([listElts([zs]); listElts([VV])])), (len([VV]) >= 0)}zs, {VV : [a] | (? Set_sub([listElts([VV]); listElts([xs])])), (VV = ys), (VV = ys), (len([VV]) = len([ys])), (listElts([VV]) = Set_cup([listElts([ys]); listElts([ys])])), (listElts([VV]) = listElts([ys])), (listElts([xs]) = Set_cup([listElts([xs]); listElts([VV])])), (listElts([xs]) = Set_cup([listElts([zs]); listElts([VV])])), (listElts([ys]) = Set_cup([listElts([ys]); listElts([VV])])), (len([VV]) >= 0)}ys) 307: where 308: ({VV : [a] | (? Set_sub([listElts([VV]); listElts([xs])])), (VV = ys), (len([VV]) = len([ys])), (listElts([VV]) = Set_cup([listElts([ys]); listElts([ys])])), (listElts([VV]) = listElts([ys])), (listElts([xs]) = Set_cup([listElts([xs]); listElts([VV])])), (listElts([xs]) = Set_cup([listElts([zs]); listElts([VV])])), (listElts([ys]) = Set_cup([listElts([ys]); listElts([VV])])), (len([VV]) >= 0)}ys, {VV : [a] | (? Set_sub([listElts([VV]); listElts([xs])])), (VV = zs), (len([VV]) = len([zs])), (listElts([VV]) = Set_cup([listElts([zs]); listElts([zs])])), (listElts([VV]) = listElts([zs])), (listElts([xs]) = Set_cup([listElts([xs]); listElts([VV])])), (listElts([xs]) = Set_cup([listElts([ys]); listElts([VV])])), (listElts([xs]) = Set_cup([listElts([ys]); listElts([VV])])), (listElts([zs]) = Set_cup([listElts([zs]); listElts([VV])])), (len([VV]) >= 0)}zs) = forall a. x1:[a] -> ({VV : [a] | (? Set_sub([listElts([VV]); listElts([x1])])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (len([VV]) >= 0)} , {VV : [a] | (? Set_sub([listElts([VV]); listElts([x1])])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (len([VV]) >= 0)})<\x1 VV -> (? Set_sub([listElts([VV]); listElts([x1])])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])])), (len([VV]) >= 0)>split {VV : [a] | (VV = xs),(len([VV]) >= 0)}xsLiquidHaskell verifies that the relevant property of `split` is
314: {-@{\ys zs -> (UnionElts xs ys zs)}> @-}The funny syntax with angle brackets simply says that the output is a a *pair* `(ys, zs)` whose union is the list of elements of the input. (**Aside** yes, this is indeed a dependent tuple; we will revisit tuples later to understand whats going on with the odd syntax.) Merge ----- Dually, we `merge` two (sorted) lists like so:
329: forall a. (GHC.Classes.Ord a) => xs:[a] -> x1:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x1]); listElts([xs])])), (listElts([VV]) = Set_cup([listElts([xs]); listElts([x1])])), (len([VV]) >= 0)}merge [a]xs [] = {VV : [a] | (VV = xs),(len([VV]) >= 0)}xs 330: merge [] ys = {VV : [a] | (len([VV]) >= 0)}ys 331: merge (x:xs) (yozz:ys) 332: | {VV : a | (VV = x)}x x:a -> y:a -> {VV : (GHC.Types.Bool) | ((? Prop([VV])) <=> (x <= y))}<= {VV : a | (VV = yozz)}yozz = {VV : a | (VV = x)}x forall <p :: a -> a -> Bool>. y:a -> ys:[a<p y>]<p> -> {VV : [a]<p> | (len([VV]) = (1 + len([ys]))), (listElts([VV]) = Set_cup([Set_sng([y]); listElts([ys])]))}: forall a. (GHC.Classes.Ord a) => xs:[a] -> x1:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x1]); listElts([xs])])), (listElts([VV]) = Set_cup([listElts([xs]); listElts([x1])])), (len([VV]) >= 0)}merge {VV : [a] | (VV = xs),(len([VV]) >= 0)}xs ({VV : a | (VV = yozz)}yozzforall <p :: a -> a -> Bool>. y:a -> ys:[a<p y>]<p> -> {VV : [a]<p> | (len([VV]) = (1 + len([ys]))), (listElts([VV]) = Set_cup([Set_sng([y]); listElts([ys])]))}:{VV : [a] | (VV = ys),(len([VV]) >= 0)}ys) 333: | otherwise = {VV : a | (VV = yozz)}yozz forall <p :: a -> a -> Bool>. y:a -> ys:[a<p y>]<p> -> {VV : [a]<p> | (len([VV]) = (1 + len([ys]))), (listElts([VV]) = Set_cup([Set_sng([y]); listElts([ys])]))}: forall a. (GHC.Classes.Ord a) => xs:[a] -> x1:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x1]); listElts([xs])])), (listElts([VV]) = Set_cup([listElts([xs]); listElts([x1])])), (len([VV]) >= 0)}merge ({VV : a | (VV = x)}xforall <p :: a -> a -> Bool>. y:a -> ys:[a<p y>]<p> -> {VV : [a]<p> | (len([VV]) = (1 + len([ys]))), (listElts([VV]) = Set_cup([Set_sng([y]); listElts([ys])]))}:{VV : [a] | (VV = xs),(len([VV]) >= 0)}xs) {VV : [a] | (VV = ys),(len([VV]) >= 0)}ysAs you might expect, the elements of the returned list are the union of the elements of the input, or as LiquidHaskell might say,
340: {-@ merge :: (Ord a) => x:[a] -> y:[a] -> {v:[a] | (UnionElts v x y)} @-}Sort ---- Finally, we put all the pieces together by
349: {-@ mergeSort :: (Ord a) => xs:[a] -> {v:[a] | (SameElts v xs)} @-} 350: forall a. (GHC.Classes.Ord a) => x1:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x1]); listElts([x1])])), (listElts([VV]) = listElts([x1])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])]))}mergeSort [] = forall <p :: a -> a -> Bool>. {VV : [{VV : a | false}]<p> | (? Set_emp([listElts([VV])])), (len([VV]) = 0)}[] 351: mergeSort [x] = {VV : [{VV : a | false}]<\_ VV -> false> | (? Set_emp([listElts([VV])])), (len([VV]) = 0), (len([VV]) >= 0)}[{VV : a | (VV = x)}x] 352: mergeSort xs = x:[a] -> y:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x]); listElts([y])]))}merge (forall a. (GHC.Classes.Ord a) => x1:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x1]); listElts([x1])])), (listElts([VV]) = listElts([x1])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])]))}mergeSort {VV : [a] | (VV = ys), (VV = ys), (len([VV]) = len([ys])), (listElts([VV]) = Set_cup([listElts([ys]); listElts([ys])])), (listElts([VV]) = listElts([ys])), (listElts([ys]) = Set_cup([listElts([ys]); listElts([VV])])), (len([VV]) >= 0)}ys) (forall a. (GHC.Classes.Ord a) => x1:[a] -> {VV : [a] | (listElts([VV]) = Set_cup([listElts([x1]); listElts([x1])])), (listElts([VV]) = listElts([x1])), (listElts([x1]) = Set_cup([listElts([x1]); listElts([VV])]))}mergeSort {VV : [a] | (VV = zs), (VV = zs), (len([VV]) = len([zs])), (listElts([VV]) = Set_cup([listElts([zs]); listElts([zs])])), (listElts([VV]) = listElts([zs])), (listElts([zs]) = Set_cup([listElts([zs]); listElts([VV])])), (len([VV]) >= 0)}zs) 353: where 354: ({VV : [a] | (VV = ys), (len([VV]) = len([ys])), (listElts([VV]) = Set_cup([listElts([ys]); listElts([ys])])), (listElts([VV]) = listElts([ys])), (listElts([ys]) = Set_cup([listElts([ys]); listElts([VV])])), (len([VV]) >= 0)}ys, {VV : [a] | (VV = zs), (len([VV]) = len([zs])), (listElts([VV]) = Set_cup([listElts([zs]); listElts([zs])])), (listElts([VV]) = listElts([zs])), (listElts([zs]) = Set_cup([listElts([zs]); listElts([VV])])), (len([VV]) >= 0)}zs) = xs:[a] -> ([a] , [a])<\ys VV -> (listElts([xs]) = Set_cup([listElts([ys]); listElts([VV])]))>split {VV : [a] | (len([VV]) >= 0)}xsThe type given to `mergeSort`guarantees that the set of elements in the output list is indeed the same as in the input list. Of course, it says nothing about whether the list is *actually sorted*. Well, Rome wasn't built in a day... [sbv]: https://github.com/LeventErkok/sbv [setspec]: https://github.com/ucsd-progsys/liquidhaskell/blob/master/include/Data/Set.spec [mccarthy]: http://www-formal.stanford.edu/jmc/towards.ps [z3cal]: http://research.microsoft.com/en-us/um/people/leonardo/fmcad09.pdf