\chapter{Abstract Refinement Types}\label{sec:abstract}

Refinement type systems, as presented so far, fall into two categories.
Expressive type systems, as presented in Section \ref{sec:undec}, 
are statically undecidable, while decidable systems, as presented in Section \ref{sec:liquid}, 
restrict the refinement language to a subset of first order logic.
%
In this section, we present abstract refinement types~\cite{Vazou13},
a means to enhance expressiveness of a refinement system,
while preserving (SMT-based) decidability.
%
The key insight is that we avail 
quantification over
the refinements of data- and function-types,
simply by encoding
refinement parameters as uninterpreted propositions within the refinement logic.
We illustrate how this mechanism yields a variety of sophisticated means for
reasoning about programs, including: 
inductive refinements for reasoning about higher-order traversal routines,
compositional refinements for reasoning about function composition,
index-dependent refinements for reasoning about key-value maps, and
recursive refinements for reasoning about recursive data types.

\section{The key idea}
Consider the monomorphic @max@ function on @Int@ values.
We give @max@ a refinement type, stating that its result
is greater or equal than both its arguments:
$$\centering\begin{tabular}{c}
\begin{code}
max     :: x:Int -> y:Int -> {v:Int | v >= x && v >= y}
max x y = if x > y then x else y
\end{code}
\end{tabular}$$
If we apply @max@ to two positive integers, 
say @n@ and @m@, we get that the result is greater or equal to both 
of them, as @max n m :: {v:Int | v >= n && v >= m}@.
However, we can not reason about an arbitrary property: 
If we apply @max@ to two even numbers, can not verify that the result is 
also even.
%
Thus, even though we have the information that both arguments are even on the input, 
we lose it on the result.

To solve this problem, we introduce \emph{abstract refinements} 
which let us 
quantify or parameterize a type over its constituent refinements.
Using abstract refinements, we can type @max@ as
$$\centering
\begin{tabular}{c}
\begin{code}
max :: forall <p::Int->Bool>. Int<p> -> Int<p> -> Int<p>
\end{code}
\end{tabular}$$
where @Int<p>@ is an abbreviation for the refinement type {@{v:Int | p(v)}@}.
Intuitively, an abstract refinement @p@ is encoded in the refinement logic 
as an \emph{uninterpreted function symbol}, which satisfies the
\emph{congruence} axiom~\cite{Nelson81}
%
$$\forall \overline{X}, \overline{Y}: (\overline{X} = \overline{Y})
\Rightarrow P(\overline{X}) = P(\overline{Y})$$
%

It is trivial to verify, with an SMT solver, that @max@ 
enjoys the above type: the input types ensure that both @p(x)@ and @p(y)@ 
hold and hence the returned value in either branch satisfies 
the refinement  @{v:Int | p(v)}@, thereby ensuring the output 
type. 

In a call site, 
we simply instantiate
the \emph{refinement} parameter of @max@ with the concrete 
refinement,
after which type checking proceeds as usual. 
%
As an example, suppose that we call @max@ with two even numbers:
$$\centering
\begin{tabular}{c}
\begin{code}
n :: {v:Int | even v}
m :: {v:Int | even v}
\end{code}
\end{tabular}$$

Then, the abstract refinement can be instantiated with a concrete predicate 
@even@, 
which will give @max@ the type
$$\centering
\begin{tabular}{c}
\begin{code}
max [even] :: 
{v:Int | even v} -> {v:Int | even v} -> {v:Int | even v}
\end{code}
\end{tabular}$$
where the expression in brackets is the refinement instantiation.
% 
Since both @n@ and @m@ are even numbers, they satisfy the function's preconditions, 
thus we can apply them to @max@, to get an even result:
$$\centering
\begin{tabular}{c}
\begin{code}
max [even] n m :: {v:Int |  even v}
\end{code}
\end{tabular}$$

This is the basic concept of abstract refinements, which 
as we shall see, have many interesting applications.

\section{Inductive Refinements}
As a first application we present how abstract refinements 
allow us to formalize induction within the type system.

Consider a @loop@ function that takes as arguments
a function @f@, an integer @n@, 
a base case @z@ and applies 
the function @f@ to the @z@, @n@ times:
%
$$\centering
\begin{tabular}{c}
\begin{code}
loop :: (Int -> a -> a) -> Int -> a -> a
loop f n z = go 0 z
  where go i acc | i < n     = go (i+1) (f i acc)
                 | otherwise = acc 
\end{code}
\end{tabular}$$
%
Now consider a user function @incr@ that uses @loop@
and at each iteration increases the accumulator by one:
$$\centering
\begin{tabular}{c}
\begin{code}
incr :: Int -> Int -> Int
incr n z = loop f n z
  where f i acc = acc + 1
\end{code}
\end{tabular}$$
%
The accumulator is initialized with @z@ and at each @loop@'s iteration
it is increased by @1@. So, at the @i@th iteration, the accumulator is equal to 
@z+i@. There will be @n@ iterations, thus the final result will be @z+n@.
%
This reasoning constitutes an inductive proof that characterizes @loop@'s behaviour.
However, 
it is unclear how to give @loop@ a (first-order) refinement type that 
describes its inductive behaviour.
Hence, it has not been possible to verify that @incr@ actually adds its two arguments.

\mypara{Typing loop}
Abstract refinements allow us to solve this problem, 
while remaining within the boundaries of SMT-based decidability.
We give @loop@ the following type:
$$\centering
\begin{tabular}{c}
\begin{code}
loop :: forall <r :: Int -> a -> Bool> .
           f : (i:Int -> a<r i> -> a<r (i+1)>) 
        -> n : {v:Int | n >= 0} 
        -> z : a<r 0> 
        -> a<r n>
\end{code}
\end{tabular}$$
The trick is to qualify over the invariant @r@ that @loop@
establishes between the loop iteration and the accumulator.
Then the type signature encodes induction on natural numbers:
(1) @n@ should be a natural number, thus a non-negative integer,
(2) the base case @z@ should satisfy the invariant at 0,
(3) in the inductive step, @f@ 
uses the old accumulator to create the new one, thus
if the old accumulator satisfies the invariant on the iteration @i@,
the new one, as constructed by @f@, should satisfy the invariant at @i+1@.
If these four conditions hold, we conclude that the result satisfies the invariant at @n@.
This scheme is not novel~\cite{coq-book}; what is new is the encoding, via uninterpreted 
predicate symbols in a SMT-decidable refinement type system.

\mypara{Using loop}
We can use this expressive type of @loop@ to verify inductive properties of
user functions:
$$
\begin{tabular}{c}
\begin{code}
incr :: n:{v:Int|v >= 0} -> z:Int -> {v:Int|v = n + z}
incr n z = loop [{\i acc -> acc + i}] f n z
  where f i acc = acc + 1
\end{code}
\end{tabular}$$
%
In the above example, the expression in brackets denotes 
the instantiation of the abstract refinement.
For purpose of illustration 
we make abstract refinement instantiation explicit, 
but it could be automatically inferred via liquid typing~\cite{Vazou13}.
%Moreover, 
%in \cite{Vazou13} we present how this inductive reasoning 
%can be applied to reason about structural inductive properties in lists.

\section{Function Composition}

As a next example, we present how one can use abstract refinements
to reason about function composition.

Consider a @plusminus@ function that composes a plus and a minus operator:
$$\centering
\begin{tabular}{c}
\begin{code}
plusminus :: n:Int 
          -> m:Int 
          -> x:Int 
          -> {v:Int | v = (x - m) + n}
plusminus n m x = (x - m) + n
\end{code}
\end{tabular}$$
In a first order refinement system we can verify that the function's behaviour
is captured by its type.
%
However, consider an alternative definition that uses function composition 
@(.) :: (b -> c) -> (a -> b) -> a -> c@.
$$\centering
\begin{tabular}{c}
\begin{code}
plusminus n m x = plus . minus
  where plus  x = x + n
        minus x = x - m
\end{code}
\end{tabular}$$
It is unclear how to give @(.)@ a (first-order) refinement type that
expresses that the result can be refined with the composition of the
refinements of both arguments results.
%
Thus, this definition of @plusminus@ can not have the previous descriptive type.

\mypara{Typing function composition}
To solve this problem, we can use abstract refinements and give @(.)@ a type:
$$\centering
\begin{tabular}{c}
\begin{code}
(.) :: forall < p :: b -> c -> Bool
              , q :: a -> b -> Bool>.
       f : (x:b -> c<p x>)
    -> g : (x:a -> b<q x>)
    -> x : a
    -> exists[z:b<q x>]. c<p z>
\end{code}
\end{tabular}$$
The trick is once again to quantify the type over 
refinements we care about.
This time, we use two abstract refinements:
the refinement @p@ of the result of the first function @f@
and the refinement @q@ of the result of the second function @g@.
For any argument @x@, we use an existential to bind the intermediate result to @z = g x@, 
so @z@ satisfies @q@ at @x@, 
and the result satisfies @p@ at the intermediate result.

\mypara{Using function composition}
With this type for function composition,
user functions get the concrete refinement of the final result
to be the composition of the two refinements of the argument functions.

Back to the @plusminus@ example, with the appropriate refinement instantiation we get 
the concrete refinement type for function composition:
$$\centering
\begin{tabular}{c}
\begin{code}
(.) [{\x v -> v = x + n}, {\x v -> v = x - m}] 
    :: f : (x:b -> {v:c | v = x+n})
    -> g : (x:a -> {v:b | v = x-m})
    -> x : a
    -> exists[z:{v:b | v = x-m}]. {v:c | v = z+n}
\end{code}
\end{tabular}$$
The result type asserts that there exists a value @z@,
which is indeed the intermediate result, with the property @z = x - m@.
With this, the final result is equal to @z + n@.
If our logic supports equality, as SMT solvers do, 
we can verify that the final result is indeed equal to @(x - m) + n@.
In other words, we can verify the desired type of @plusminus@. 

\section{Index-Dependent Invariants}\label{sec:overview:index}

Next, we illustrate how abstract invariants allow us to 
specify and verify index-dependent invariants of key-value maps. 
To this end, we encode vectors
as functions from @Int@ to 
some generic range @a@. Formally, we specify vectors as 
%
$$\centering\begin{tabular}{c}\begin{code}
data Vec a <dom :: Int -> Bool, rng :: Int -> a -> Bool> 
  = V (i:Int<dom> -> a <rng i>)
\end{code}\end{tabular}$$
%
Here, we are parameterizing the definition of the type @Vec@ 
with two abstract refinements, @dom@ and @rng@, which
respectively describe the \emph{domain} and \emph{range} of the vector.
That is, @dom@ describes the set of valid indices, 
and @rng@ specifies an invariant relating each @Int@ index
with the value stored at that index.

\mypara{Describing Vectors}
With this encoding, we can describe various vectors. 
To start with we can have vectors of @Int@ defined on positive integers
with values equal to their index:
%
$$\centering\begin{tabular}{c}\begin{code}
Vec <{\v -> v > 0}, {\_ v -> v = x}> Int
\end{code}\end{tabular}$$
%
Or a vector that is defined only on index 1 with value 12:
%
$$\centering\begin{tabular}{c}\begin{code}
Vec <{\v -> v = 1}, {\_ v -> v = 12}> Int
\end{code}\end{tabular}$$
%
As a more interesting example, we can define a \textit{Null Terminating String}
with length @n@, 
as a vector of @Char@ defined on a range @[0, n)@ 
with its last element equal to the terminating character:
%
$$\centering\begin{tabular}{c}\begin{code}
Vec <{\v -> 0 <= v < n}
    ,{\i v -> i = n-1 => v = `\0`}> Char
\end{code}\end{tabular}$$
%
Finally, we can encode a \textit{Fibonacci memoization vector}, 
which can be used to efficiently compute a Fibonacci number,
that is defined
on positive integers and its value on index @i@ is either zero
or the @i@th Fibonacci number:
%
$$\centering\begin{tabular}{c}\begin{code}
Vec <{\v -> 0 <= v}
    ,{\i v -> v != 0 => v = fib(i)}> Char
\end{code}\end{tabular}$$

\mypara{Using Vectors}
A first step towards using vectors is 
to supply the appropriate types 
for vector operations, (e.g.,
set, get and empty).
This usually means qualifying
over the domain and the range of the vectors. 
%
Then, the programmer has to specify interesting vector properties, 
as we did for the Fibonacci memoization, or the null terminating string.
% 
Finally, the system can verify that user functions, that transform vectors,
preserve these properties.
%
This procedure is applied in \cite{Vazou13},
where, with the appropriate types for vector operations,
we reason about functions that transform null terminating strings
or efficiently compute a Fibonacci number.
%


\section{Recursive Invariants}\label{sec:abstract}
Finally, we describe how we use abstract refinements to reason about 
properties of recursive data structures.
%
For the purpose of illustration, we define a refined version of a @List@ datatype with values
of type @a@:
$$\centering\begin{tabular}{c}\begin{code}
data List a <p :: a -> a -> Bool>
  = N
  | C (hd :: a) (tl :: List <p> (a <p h>))
\end{code}\end{tabular}$$
We are parametrizing the @List@ over an abstract refinement @p@
that relates two elements of type @a@.
With this, the list is either the empty list @N@, 
or contains a head @hd@ of type
@a@ and a tail @tl@ which is a list of elements of type @a<p h>@, 
i.e., these elements satisfy the abstract refinement @p@ at the head.
Since this definition is recursively applied, 
the abstract refinement @p@ holds
between each pair of elements in the list.

\mypara{Unfolding Lists}
To demonstrate the previous property, 
we will unfold a @List@ with three elements 
that satisfies an abstract refinement @p@.
Consider such a list:
%
$$\centering\begin{tabular}{c}\begin{code}
C h1 (C h2 (C h3 N)) :: List <p> a
\end{code}\end{tabular}$$
%
If we unfold this list once, by the definition of the @C@ data constructor, 
the first element is of type @a@, 
while the rest is a list with values
that satisfy @p@ at the first element,
i.e., @(C h2 (C h3 N)) :: List <p> a<p h1>@.
%
With a second unfold
we get that the second element 
satisfies @p@ at the first element,
i.e., \hbox{@h2::a<p h1>@},
while the rest is a list with values
that satisfy @p@ at both the first and the second element, 
i.e., \hbox{@C h3 N :: List <p> a<p h1 && p h2>@}.
%
With the last unfold we get that the last element satisfies @p@
at all the previous elements,
i.e., \hbox{@h3 :: a<p h1 && p h2>@},
while the empty list satisfies @p@ at every list element,
i.e., 
\hbox{@N  :: List <p> a<p h1 && p h2 && p3>@},
which holds as by its definition the empty list @N@ satisfies any refinement.

Thus, @p@ holds between every pair of the list, 
as for any two two elements @hi@ and @hj@, with @i < j@, at the @i@th unfold
@hj@ satisfies @p@ at @hi@.

If we instantiate the abstract refinement @p@ with the concrete refinement 
\hbox{@{\h v -> h <= v}@},
that expresses that each values is greater than the head, 
we get that 
each element is greater than all its previous in the list.
So we describe an increasing list:
%
$$\centering\begin{tabular}{c}\begin{code}
type IncrList a = List <{\h v -> h <= v}> a
\end{code}\end{tabular}$$
%
We can describe different list properties,
by embedding appropriate concrete refinements.
For instance, if we use a refinement that expresses that each value 
is less than the head, i.e., \hbox{@{\h v -> h >= v}@} or 
different from it, i.e., \hbox{@{\h v -> h ~= v}@},
we can describe decreasing or unique element lists.

\mypara{Using Lists}
We can use the refined type for lists 
to verify list properties.
As an example, our system can verify that the following 
inserting sort algorithm actually returns 
an increasing list.
%
$$\centering\begin{tabular}{c}\begin{code}
insertSort :: (Ord a) => [a] -> IncrList a 
insertSort = foldr insert N

insert :: (Ord a) => a -> IncrList a -> IncrList a 
insert y N       = C y N
insert y (C x xs) | y <= x        = C y (C x xs)
                  | otherwise     = C x (insert y xs)
\end{code}\end{tabular}$$

\begin{comment}
It is important to note that abstract refinements allow us to distinguish the list definition
from the invariants that hold.
%Thus, we can use the same list definition with different properties in different point of the program.
As a consequence, they enable verification of complex algorithms
that establish different invariance for lists,
such as the official Ghc's sorting algorithm, 
that uses both increasing and decreasing lists.
\end{comment}

\section{Formal Language}
We suggest that any refinement system can be extended with abstract refinements
without increasing its complexity.
%
First of all, the syntax should be extended to support refinement abstraction
and application.
In the case of refinement abstraction, we abstract from an expression $e$
the refinement $\pi$ with type $\tau$, while in refinement application
we instantiate an abstrast refinement with a concrete one $p$
that may have some parameters $\bar{x}$.
%
The predicates of the language should be extended to 
include abstract refinements, applied to program expressions.
%
The types of the language should also be extended to include 
refinement abstraction.
%

Since we extended our expressions the relevant typing rules should be added.
The refinement abstraction expression is typed as an refinement abstraction
type, the abstract refinement is treated as a variable
and the checking proceeds in a straightforward way.
In the refinement application, the abstract refinement $\pi$ is replaced with a concrete one
over the type $\tau$. A formal definition of this substitution can be found in our paper\cite{Vazou13}.

Similarly, since we extended our types, the well-formedness and subtyping 
rules should be extended.
In both cases, the abstract refinement is added in the environment
and the check proceeds in a straightforward way.

We note that
abstract refinements 
can be treated as uninterprented functions in the implication
checking algorithm, thus the complexity of the system is not increased.
Moreover, they appear only in the types, thus they can be erased at run-time.
\begin{figure}[ht!]
\centering
$$
\begin{array}{rrcl}
\emphbf{Expressions} \quad 
  & e 
  & ::= 
  & 		 \dots
  \spmid \epabs{\rvar}{\tau}{e}
  \spmid \epapp{e}{\efunbar{x:\tau_x}{p}} 
  \\[0.05in] 

\emphbf{Predicates} \quad 
  & p
  & ::= 
  &		\dots
  \spmid \rvapp{\rvar}{e}  
  \\[0.05in] 
\emphbf{Types} \quad 
  & \tau 
  & ::= 
  &		 \dots
  \spmid \tpabs{\rvar}{\tau}{\tau}

\end{array}
$$
\caption{\textbf{Syntax of Expressions, Types and Schemas}}
\label{fig:syntax}
\end{figure}


\begin{figure}[ht!]

\medskip \judgementHead{Type Checking}{$\hastype{\Gamma}{e}{\tau}$}
$$\begin{array}{cc}
\inference
    {\hastype{\Gamma, \rvar:\tau_\rvar}{e}{\tau} &&
     \isWellFormed{\Gamma}{\tau} 
    }
    {\hastype{\Gamma}{\epabs{\rvar}{\tau_\rvar}{e}}{\tpabs{\rvar}{\tau_\rvar}{\tau}}}
    [\tpgen]
&  

\inference
    {\hastype{\Gamma}{e}{\tpabs{\rvar}{\tau_\rvar}{\tau}} && 
     \hastype{\Gamma}{\efunbar{x:\tau_x}{p}}{\tau_\rvar}
    }
    {\hastype{\Gamma}
             {\epapp{e}{\efunbar{x:\tau_x}{p}}}
             {\rpinst{\tau}{\rvar}{\efunbar{x:\tau_x}{p}}}
    }
    [\tpinst]
\end{array}$$

\medskip \judgementHead{Subtyping}{\isSubType{\Gamma}{\tau}{\tau}}
$$
\inference
   {(\Gamma, v:b \vdash \texttt{Valid}(p_1 \Rightarrow p_2))}
   {\isSubType{\Gamma}{\tref{b}{p_1}}{\tref{b}{p_2}}}
   [\tsubBase]
$$
\caption{\textbf{Static Semantics} from $\lambda_C$ to $\lambda_A$}
\label{fig:rules}
\end{figure}
