Abstract Refinements {#induction} ================================= Induction --------- Encoding *induction* with Abstract refinements Induction ========= Example: `loop` redux --------------------- Recall the *higher-order* `loop`
\begin{code} loop lo hi base f = go lo base where go i acc | i < hi = go (i+1) (f i acc) | otherwise = acc \end{code} Iteration Dependence -------------------- We used `loop` to write \begin{code}
{-@ add :: n:Nat -> m:Nat -> {v:Nat|v=m+n} @-} add n m = loop 0 m n (\_ i -> i + 1) \end{code}
**Problem** - As property only holds after *last* loop iteration... - ... cannot instantiate `α` with `{v:Int | v = n + m}`
Iteration Dependence -------------------- We used `loop` to write \begin{code}
{-@ add :: n:Nat -> m:Nat -> {v:Nat|v=m+n} @-} add n m = loop 0 m n (\_ i -> i + 1) \end{code}
**Problem** Need invariant relating *iteration* `i` with *accumulator* `acc` Iteration Dependence -------------------- We used `loop` to write \begin{code}
{-@ add :: n:Nat -> m:Nat -> {v:Nat|v=m+n} @-} add n m = loop 0 m n (\_ i -> i + 1) \end{code}
**Solution** - Abstract Refinement `p :: Int -> a -> Prop` - `(p i acc)` relates *iteration* `i` with *accumulator* `acc` Induction in `loop` (by hand) ----------------------------- \begin{code}
loop lo hi base f = go lo base where go i acc | i < hi = go (i+1) (f i acc) | otherwise = acc \end{code}
------------ --- ---------------------------- **Assume** : `out = loop lo hi base f` **Prove** : `(p hi out)` ------------ --- ----------------------------
Induction in `loop` (by hand) ----------------------------- \begin{code}
loop lo hi base f = go lo base where go i acc | i < hi = go (i+1) (f i acc) | otherwise = acc \end{code}
**Base Case:**   Initial accumulator `base` satisfies invariant `(p lo base)` Induction in `loop` (by hand) ----------------------------- \begin{code}
loop lo hi base f = go lo base where go i acc | i < hi = go (i+1) (f i acc) | otherwise = acc \end{code}
**Inductive Step:**   `f` *preserves* invariant at `i` `(p i acc) => (p (i+1) (f i acc))` Induction in `loop` (by hand) ----------------------------- \begin{code}
loop lo hi base f = go lo base where go i acc | i < hi = go (i+1) (f i acc) | otherwise = acc \end{code}
**"By Induction"**   `out` satisfies invariant at `hi` `(p hi out)` Induction in `loop` (by type) ----------------------------- Induction is an **abstract refinement type** for `loop`
\begin{code} {-@ loop :: forall a

a -> Prop>. lo:Int -> hi:{Int | lo <= hi} -> base:a

-> f:(i:Int -> a

-> a

) -> a

@-} \end{code}
Induction in `loop` (by type) ----------------------------- `p` is the *index dependent* invariant! \begin{code}
p :: Int -> a -> Prop -- invt base :: a

-- base f :: i:Int -> a

-> a -- step out :: a

-- goal \end{code} Using Induction --------------- \begin{code} {-@ add :: n:Nat -> m:Nat -> {v:Nat| v=m+n} @-} add n m = loop 0 m n (\_ z -> z + 1) \end{code}
**Verified** by *instantiating* the abstract refinement of `loop` `p := \i acc -> acc = i + n` Generalizes To Structures ------------------------- Same idea applies for induction over *structures* ... Structural Induction ==================== Example: Lists --------------
\begin{code} data L a = N | C a (L a) \end{code}

Lets write a generic loop over such lists ...
Example: `foldr` ---------------- \begin{code}
foldr f b N = b foldr f b (C x xs) = f xs x (foldr f b xs) \end{code}
Lets brace ourselves for the type...
Example: `foldr` ---------------- \begin{code} {-@ foldr :: forall a b

b -> Prop>. (xs:_ -> x:_ -> b

-> b) -> b

-> ys:L a -> b

@-} foldr f b N = b foldr f b (C x xs) = f xs x (foldr f b xs) \end{code}

Lets step through the type...
`foldr`: Abstract Refinement ---------------------------- \begin{code}
p :: L a -> b -> Prop step :: xs:_ -> x:_ -> b

-> b base :: b

ys :: L a out :: b

\end{code}
`(p xs b)` relates `b` with **folded** `xs` `p :: L a -> b -> Prop` `foldr`: Base Case ------------------ \begin{code}

p :: L a -> b -> Prop step :: xs:_ -> x:_ -> b

-> b base :: b

ys :: L a out :: b

\end{code}
`base` is related to **empty** list `N` `base :: b

` `foldr`: Ind. Step ------------------ \begin{code}

p :: L a -> b -> Prop step :: xs:_ -> x:_ -> b

-> b base :: b

ys :: L a out :: b

\end{code}
`step` **extends** relation from `xs` to `C x xs` `step :: xs:_ -> x:_ -> b

-> b

` `foldr`: Output --------------- \begin{code}

p :: L a -> b -> Prop step :: xs:_ -> x:_ -> b

-> b base :: b

ys :: L a out :: b

\end{code}
Hence, relation holds between `out` and **entire input** list `ys` `out :: b

` Using `foldr`: Size ------------------- We can now verify
\begin{code} {-@ size :: xs:_ -> {v:Int| v = (llen xs)} @-} size = foldr (\_ _ n -> n + 1) 0 \end{code}

by *automatically instantiating* `p := \xs acc -> acc = (llen xs)`
Using `foldr`: Append --------------------- We can now verify
\begin{code} {-@ (++) :: xs:_ -> ys:_ -> (Cat a xs ys) @-} xs ++ ys = foldr (\_ -> C) ys xs \end{code}
where \begin{code} {-@ type Cat a X Y = {v:_|(llen v) = (llen X) + (llen Y)} @-} \end{code}

By automatically instantiating `p := \xs acc -> llen acc = llen xs + llen ys`
Recap ----- Abstract refinements *decouple* **invariant** from **traversal**
**Reusable** specifications for higher-order functions.

**Automatic** checking and instantiation by SMT.
Recap ----- 1. Refinements: Types + Predicates 2. Subtyping: SMT Implication 3. Measures: Strengthened Constructors 4. Abstract: Refinements over Type Signatures +
**Functions**
+
**Data** [continue]