% Simple Refinement Types Simple Refinement Types ----------------------- \begin{code} module SimpleRefinements where import Language.Haskell.Liquid.Prelude \end{code} Simple Refinement Types ----------------------- We use special comments to give specifications, as *refinement types*. This type describes `Int` values that equal `0`. \begin{code} {-@ zero :: {v:Int | v = 0} @-} zero :: Int zero = 0 \end{code} Refinements are *logical formulas* ---------------------------------- If - refinement of `T1` **implies** refinement of `T2` - `p1 => p2` Then - `T1` is a **subtype** of `T2` - `{v:t | p1} <: {v:t | p2}` Refinements are *logical formulas* ---------------------------------- For example, since - `v = 0` *implies* `v >= 0` Therefore - `{v:Int | v = 0} <: {v:Int | v >= 0}` Refinements are *logical formulas* ---------------------------------- \begin{code} So we can have a type for natural numbers:
type Nat = {v:Int | v >= 0} \end{code}
And, via SMT based subtyping LiquidHaskell verifies:
\begin{code} {-@ zero' :: Nat @-} zero' :: Int zero' = 0 \end{code} Lists: Universal Invariants --------------------------- Constructors enable *universally quantified* invariants. For example, we define a list: \begin{code} infixr `C` data L a = N | C a (L a) \end{code}
And specify that, *every element* in a list is non-negative: \begin{code} {-@ natList :: L Nat @-} natList :: L Int natList = 0 `C` 1 `C` 3 `C` N \end{code} Demo: Lets see what happens if `natList` contained a negative number. Refinement Function Types ------------------------- Consider a `safeDiv` operator:
\begin{code} safeDiv :: Int -> Int -> Int safeDiv x y = x `div` y \end{code}
We can use refinements to specify a **precondition**: divisor is **non-zero**
\begin{code} {-@ safeDiv :: Int -> {v:Int | v != 0} -> Int @-} \end{code}
Demo: Lets see what happens if the preconditions cannot be proven. Dependent Function Types ------------------------ \begin{code} Consider a list indexing function: (!!) :: L a -> Int -> a (C x _) !! 0 = x (C _ xs)!! n = xs!!(n-1) _ !! _ = liquidError "This should not happen!" \end{code}
We desire a **precondition** that index `i` be between `0` and **list length**. We use **measures** to talk about the length of a list in **logic**.