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 base lo)` 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 acc i) => (p (f i acc) (i + 1))` 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 out hi)` Induction in `loop` (by type) ----------------------------- Induction is 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}
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` 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 `p := \i acc -> acc = i + n` -
**Base:** `n = 0 + n`
-
**Step:** `acc = i + n => acc + 1 = (i + 1) + n`
-
**Goal:** `out = m + 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

` states `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:L a -> x:a -> 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}
Relation holds between `out` and 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}

Verified by automatically instantiating `p := \xs acc -> acc = (llen xs)`
Using `foldr`: Append --------------------- \begin{code} {-@ (++) :: xs:_ -> ys:_ -> (Cat a xs ys) @-} xs ++ ys = foldr (\_ -> C) ys xs \end{code}
where the alias `Cat` is:
\begin{code} {-@ type Cat a X Y = {v:L a|(llen v) = (llen X) + (llen Y)} @-} \end{code}
Using `foldr`: Append --------------------- \begin{code}
{-@ (++) :: xs:_ -> ys:_ -> (Cat a xs ys) @-} xs ++ ys = foldr (\_ -> C) ys xs \end{code}
Verified 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 + *Dependencies* +
*Induction*