Abstract Refinements {#data}
============================
Recap
-----
**So far**
Abstract Refinements decouple invariants from
+ functions
+ indexed data
**Next**
Decouple invariants from **recursive** data structures
Decouple Invariants From Data {#recursive}
==========================================
{#asd}
-------
Recursive Structures
--------------------
Lets see another example of decoupling...
\begin{code}
{-# LANGUAGE NoMonomorphismRestriction #-}
module List (insertSort) where
{-@ LIQUID "--no-termination" @-}
mergeSort :: Ord a => [a] -> [a]
insertSort :: (Ord a) => [a] -> L a
slist :: L Int
slist' :: L Int
iGoUp, iGoDn, iDiff :: [Int]
infixr `C`
\end{code}
Decouple Invariants From Recursive Data
=======================================
Recall: Lists
-------------
\begin{code}
data L a = N
| C { hd :: a, tl :: L a }
\end{code}
Recall: Refined Constructors
----------------------------
Define **increasing** Lists with strengthened constructors:
\begin{spec}
data L a where
N :: L a
C :: hd:a -> tl: L {v:a | hd <= v} -> L a
\end{spec}
Problem: Decreasing Lists?
--------------------------
What if we need *both* [increasing *and* decreasing lists?](http://hackage.haskell.org/package/base-4.7.0.0/docs/src/Data-List.html#sort)
[Separate (indexed) types](http://web.cecs.pdx.edu/~sheard/Code/QSort.html) get quite complicated ...
Abstract That Refinement!
-------------------------
\begin{code}
{-@ data L a a -> Prop>
= N
| C { hd :: a, tl :: L
a
} @-}
\end{code}
`p` is a **binary relation** between two `a` values
Definition relates `hd` with **all** the elements of `tl`
Recursive: `p` holds for **every pair** of elements!
Example
-------
Consider a list with *three* or more elements
\begin{spec}
x1 `C` x2 `C` x3 `C` rest :: L a
\end{spec}
Example: Unfold Once
--------------------
\begin{spec}
x1 :: a
x2 `C` x3 `C` rest :: L
a
\end{spec}
Example: Unfold Twice
---------------------
\begin{spec}
x1 :: a
x2 :: a
x3 `C` rest :: L
a
\end{spec}
Example: Unfold Thrice
----------------------
\begin{spec}
x1 :: a
x2 :: a
x3 :: a
rest :: L
a
\end{spec}
Note how `p` holds between **every pair** of elements in the list.
A Concrete Example
------------------
That was a rather *abstract*!
How can we *use* fact that `p` holds between *every pair*?
**Instantiate** `p` with a *concrete* refinement
Example: Increasing Lists
-------------------------
**Instantiate** `p` with a *concrete* refinement
\begin{code}
{-@ type IncL a = L <{\hd v -> hd <= v}> a @-}
\end{code}
Refinement says: `hd` less than **every** `v` in tail,
i.e., `IncL` denotes **increasing** lists.
Example: Increasing Lists
-------------------------
LiquidHaskell *verifies* that `slist` is indeed increasing...
\begin{code}
{-@ slist :: IncL Int @-}
slist = 1 `C` 6 `C` 12 `C` N
\end{code}
... and *protests* that `slist'` is not:
\begin{code}
{-@ slist' :: IncL Int @-}
slist' = 6 `C` 1 `C` 12 `C` N
\end{code}
Insertion Sort
--------------
\begin{code}
{-@ insertSort :: (Ord a) => [a] -> IncL a @-}
insertSort = foldr insert N
insert y N = y `C` N
insert y (x `C` xs)
| y < x = y `C` (x `C` xs)
| otherwise = x `C` insert y xs
\end{code}
(Mouseover `insert` to see inferred type.)
Checking GHC Lists
------------------
Demo:
Above applies to GHC's List definition:
\begin{spec}
data [a] a -> Prop>
= []
| (:) { h :: a, tl :: [a
]
}
\end{spec}
Checking GHC Lists
------------------
Increasing Lists
\begin{code}
{-@ type Incs a = [a]<{\h v -> h <= v}> @-}
{-@ iGoUp :: Incs Int @-}
iGoUp = [1,2,3]
\end{code}
Checking GHC Lists
------------------
Decreasing Lists
\begin{code}
{-@ type Decs a = [a]<{\h v -> h >= v}> @-}
{-@ iGoDn :: Decs Int @-}
iGoDn = [3,2,1]
\end{code}
Checking GHC Lists
------------------
Duplicate-free Lists
\begin{code}
{-@ type Difs a = [a]<{\h v -> h /= v}> @-}
{-@ iDiff :: Difs Int @-}
iDiff = [1,3,2]
\end{code}
Checking Lists
--------------
Now we can check all the usual list sorting algorithms
Demo: List Sorting