liquidhaskell-0.8.0.2: docs/slides/niki/lhs/Array.lhs
%Indexed-Dependent Refinements
Indexed-Dependent Refinements
-----------------------------
\begin{code}
module LiquidArray where
import Language.Haskell.Liquid.Prelude (liquidAssume)
{-@ LIQUID "--no-termination" @-}
\end{code}
Indexed-Dependent Refinements
-----------------------------
We define a Vector of `a`s
implemented as a function from `Int` to `a`s <br>
<br>
\begin{code}
data Vec a = V (Int -> a)
\end{code}
Abstract Over the Domain and Range
----------------------------------
We parameterize the definition with two abstract refinements:
<br>
<br>
\begin{code}
{-@ data Vec a <dom :: Int -> Prop, rng :: Int -> a -> Prop>
= V {a :: i:Int<dom> -> a <rng i>}
@-}
\end{code}
- `dom`: describes the *domain*
- `rng`: describes each value with respect to its index
Describing Vectors
------------------
\begin{code}By instantiating these two predicates, we describe Vector's *domain* and *range*
{-@ data Vec a <dom :: Int -> Prop, rng :: Int -> a -> Prop>
= V {a :: i:Int<dom> -> a <rng i>}
@-}
\end{code}
<br>
A vector of `Int` *defined on* values less than `42`
*containing values* equal to their index:
<br>
<br>
\begin{code}
{-@ type IdVec =
Vec <{\v -> (v < 42)}, {\j v -> (v = j)}> Int
@-}
\end{code}
Describing Vectors
------------------
\begin{code}By instantiating these two predicates, we describe Vector's *domain* and *range*
{-@ data Vec a <dom :: Int -> Prop, rng :: Int -> a -> Prop>
= V {a :: i:Int<dom> -> a <rng i>}
@-}
\end{code}
<br>
A vector *defined on* the range `[0..n)` with its *last element* equal to `0`:
<br>
<br>
\begin{code}
{-@ type ZeroTerm N =
Vec <{\v -> (0 <= v && v < N)}, {\j v -> (j = N - 1 => v = 0)}> Int
@-}
\end{code}
Describing Vectors
------------------
\begin{code}By instantiating these two predicates, we describe Vector's *domain* and *range*
{-@ data Vec a <dom :: Int -> Prop, rng :: Int -> a -> Prop>
= V {a :: i:Int<dom> -> a <rng i>}
@-}
\end{code}
<br>
A vector *defined on* integers whose *value at index `j`* is either
`0` or the `j`th fibonacci:
<br>
<br>
\begin{code}
{-@ type FibV =
Vec <{\v -> 0=0}, {\j v -> ((v != 0) => (v = (fib j)))}> Int
@-}
\end{code}
Operations on Vectors
---------------------
<a href="http://goto.ucsd.edu:8090/index.html#?demo=Array.hs" target= "_blank">Demo:</a>
We give appropriate types to vector operations (empty, set, get...)
- This means *abstracting* over the domain and range
Empty
-----
`empty` returns a Vector whose domain is always false
<br>
<br>
\begin{code}
{-@ empty :: forall <p :: Int -> a -> Prop>. Vec < {v:Int | 0=1}, p> a @-}
empty :: Vec a
empty = V $ \_ -> (error "Empty array!")
\end{code}
Typing Get
----------
If `i` satisfies the domain then
if we `get` the `i`th element of an array,
the result should satisfy the range at `i`
<br>
<br>
\begin{code}
{-@ get :: forall a <r :: Int -> a -> Prop, d :: Int -> Prop>.
i: Int<d>
-> a: Vec<d, r> a
-> a<r i> @-}
get :: Int -> Vec a -> a
get i (V f) = f i
\end{code}
Typing Set
----------
If `i` satisfies the domain then
if we `set` the `i`th element of a Vector to a value
that satisfies range at `i`,
then Vector's domain will be extended with `i`
<br>
<br>
\begin{code}
{-@ set :: forall a <r :: Int -> a -> Prop, d :: Int -> Prop>.
i: Int<d>
-> x: a<r i>
-> a: Vec < {v:Int<d> | v != i}, r> a
-> Vec <d, r> a @-}
set :: Int -> a -> Vec a -> Vec a
set i v (V f) = V $ \k -> if k == i then v else f k
\end{code}
Using Vectors
-------------
\begin{code}Remember the fibonacci memoization Vector:
type FibV =
Vec <{\v -> 0=0}, {\j v -> ((v != 0) => (v = (fib j)))}> Int
\end{code}
<br>
Where `fib` is an *uninterprented function*
\begin{code}
{-@ measure fib :: Int -> Int @-}
\end{code}
<br>
We used `fib` to define the `axiom_fib`
\begin{code}
{-@ predicate Fib I =
(fib i) = (if (i <= 1) then 1 else ((fib (i-1)) + (fib (i-2))))
@-}
{-@ assume axiom_fib :: i:Int -> {v: Bool | ((Prop v) <=> (Fib i))} @-}
axiom_fib :: Int -> Bool
axiom_fib i = undefined
\end{code}
Fast Fibonacci
--------------
Now we can efficiently compute the `i`th fibonacci number
\begin{code}
{-@ fastFib :: x:Int -> {v:Int | v = fib(x)} @-}
fastFib :: Int -> Int
fastFib n = snd $ fibMemo (V (\_ -> 0)) n
\end{code}
Fibonacci Memo
--------------
\begin{code}
{-@ fibMemo :: FibV -> i:Int -> (FibV, {v: Int | v = (fib i)}) @-}
fibMemo :: Vec Int -> Int -> (Vec Int, Int)
fibMemo t i
| i <= 1
= (t, liquidAssume (axiom_fib i) (1 :: Int))
| otherwise
= case get i t of
0 -> let (t1, n1) = fibMemo t (i-1)
(t2, n2) = fibMemo t1 (i-2)
n = liquidAssume (axiom_fib i) (n1 + n2)
in (set i n t2, n)
n -> (t, n)
\end{code}