% Inductive Refinements
Inductive Refinements
---------------------
\begin{code}
module Loop where
import Prelude hiding ((!!), foldr, length, (++))
import Measures
import Language.Haskell.Liquid.Prelude
{-@ LIQUID "--no-termination" @-}
\end{code}
`loop` Revisited
----------------
Recall the **higher-order** `loop` function
\begin{code}
loop :: Int -> Int -> a -> (Int -> a -> a) -> a
loop lo hi base f = go lo base
where go i acc | i < hi = go (i+1) (f i acc)
| otherwise = acc
\end{code}
We used `loop` to write
\begin{code}
{-@ add :: n:Nat -> m:{v:Int| v >= 0} -> {v:Int| v = m + n} @-}
add :: Int -> Int -> Int
add n m = loop 0 m n (\_ i -> i + 1)
\end{code}
**Problem:** Verification requires an **index dependent loop invariant** `p`
- Which relates index `i` with accumulator `acc`: formally `(p acc i)`
Loop Invariants and Induction
-----------------------------
\begin{code} Recall the **higher-order** `loop` function
loop :: Int -> Int -> a -> (Int -> a -> a) -> a
loop lo hi base f = go lo base
where go i acc | i < hi = go (i+1) (f i acc)
| otherwise = acc
\end{code}
To verify output satisfies relation at `hi` we prove that **if**
- **base case** initial accumulator `base` satisfies invariant at `lo`
- `(p base lo)`
- **induction step** `f` **preserves** the invariant at `i`
- **if** `(p acc i)` then `(p (f i acc) (i+1))`
- **then** "by induction" result satisfies invariant at `hi`
- `(p (loop lo hi base f) hi)`
Encoding Induction With Abstract Refinements
--------------------------------------------
We capture induction with an **abstract refinement type** for `loop`
\begin{code}
{-@ loop :: forall a
a -> Prop>. lo:Int -> hi:{v:Int|lo <= v} -> base:a
-> f:(i:Int -> a
-> a
) -> a
@-}
\end{code}
\begin{code} `p` is the index dependent invariant!
p :: Int -> a -> Prop> -- ind hyp
base :: a
-- base case f :: (i:Int -> a
-> a
) -- ind. step out :: a
-- output holds at hi
\end{code}
Encoding Induction With Abstract Refinements
--------------------------------------------
\begin{code} Lets revisit
add n m = loop 0 m n (\_ z -> z + 1)
\end{code}
The invariant is: `p` instantiated with `\i acc -> acc = i + n`
**base case:** `(p 0 n)` holds as `n = 0 + n`
**ind. step** `\_ z -> z + 1` preserves invariant
- `acc = i + n` *implies* `acc + 1 = (i + 1) + n`
**output** hence, `loop 0 m n (\_ z -> z + 1) = m + n`
Which lets us verify that
\begin{code}.
add :: n:Nat -> m:Nat -> {v:Int| v = m + n}
\end{code}
Structural Induction With Abstract Refinements
----------------------------------------------
Same idea applies for induction over *structures*
We define a `foldr` function that resembles loop.
\begin{code}
\end{code}
\begin{code}
{-@ foldr :: forall a b
b -> Prop>. (xs:L a -> x:a -> b
-> b
) -> b
-> ys: L a -> b
@-}
foldr :: (L a -> a -> b -> b) -> b -> L a -> b
foldr f b N = b
foldr f b (C x xs) = f xs x (foldr f b xs)
\end{code}
- **base case** `b` is related to nil `N`
- **ind. step** `f` extends relation over cons `C`
- **output** relation holds over entire list `ys`
Structural Induction With Abstract Refinements
----------------------------------------------
We can now verify
\begin{code}
{-@ size :: xs:L a -> {v: Int | v = (llen xs)} @-}
size :: L a -> Int
size = foldr (\_ _ n -> n + 1) 0
\end{code}
Here, the relation
- `(p xs acc)`
is **automatically instantiated** with
- `acc = (llen xs)`
Structural Induction With Abstract Refinements
----------------------------------------------
Similarly we can now verify
\begin{code}_
{-@ ++ :: xs:L a -> ys:L a -> {v:L a | (llen v) = (llen xs) + (llen ys)} @-}
xs ++ ys = foldr (\_ z zs -> C z zs) ys xs
\end{code}
Here, the relation
- `(p xs acc)`
is **automatically instantiated** with
- `(llen acc) = (llen xs) + (llen ys)`