Library HTT0

Imperative Programming in Coq

Just as in Haskell, we can simulate writing programs that manipulate
state by taking advantage of monads. Another way to think about this
is that we can embed the primitives of IMP into Coq and we automatically
get a higher-order, dependently-typed, stateful programming language.

In the development below, I will build a module that is parameterized by
some universe of values that we can store in the heap, and then later
instantiate the universe with a type of my choice. In the module, we
will define basic notions of a heap, of commands as a monad which
manipulates heaps, and appropriate Hoare-logic rules for reasoning
about these commands.

Allocation -- to allocate a fresh location, we run through
the heap and find the biggest pointer, and simply return
the next biggest pointer. Another good homework is to
change the definitions here to carry along the "next available
pointer" as part of the state of the system.

Hoare Logic

The Hoare Total-Correctness Triple {{P}}c{{Q}} holds when
if we run c in a heap h satisfying P, we get back a heap
h' and value v, satisfying Qhvh'. Notice that our
post-conditions allow us to relate the input heap, the output
value, and the output heap. The ability to refer to the initial
heap is important for giving rules that are as strong as possible
without having to introduce auxilliary variables.

The Hoare-rule for return: We can run the command in any initial state
and end up in a state where the heap is unchanged, and the return value
is equal to the value we passed in. This is pretty obviously the
weakest precondition needed to ensure the command won't fail, and
the strongest post-condition we can show about the resulting state.

The newu command can be run in any initial state h, and
results in a state (p,u)::h where p is fresh. The freshness
is captured by the fact that lookuphp = None, i.e., the
pointer was unallocated in the pre-state.

The rule for bind is the most complicated, but that's more
because we want to support dependency than anything else.
Intuitively, if {{P1}}c{{Q1}} and {{P2x}}fx{{Q2x}}, then
the compound command x <- c ; f has as the weakest pre-
condition needed to ensure we don't fail that P1 holds and
for any (demonically-chosen state and value) x and h' which
c might compute (and hence satisfies Q1hxh'), we can
show that P2xh' holds. Both conditions are needed to ensure
that neither command will fail.

The post-condition is the strongest post-condition we can
calculate as the composition of the commands. It is effectively
the relational composition of the post-conditions for c and
f respectively.

Again, note that we can effectively compute the pre- and post-
conditions instead of forcing a prover to magically come up
with appropriate conditions.

Alas, our universe of storable values cannot be big enough
to store computations. If we try to add computations to the
types in U, we get a non-positive occurrence. In short, you
seem to need generally recursive types to build storable
commands. Not suprisingly, this leads to termination problems,
as we can use Landin's knot to build a diverging computation...

More generally, we can write a function, like swap, and give
it a human-readable specification. Then we can use the
combinators to build most of the proof, and all we are left
with are the two verification conditions from the rule of consequence.

Finally, if we can show that a loop body has pre-condition P and
post-condition Q, then we can conclude that iterating the body n
times results in a command with pre-condition obtained by
iter_precompPQ and post-condition obtained by iter_postcompQ.

Now consider where we have two points, and some information about
both of the pointers. Unfortunately, our specification for inc
is too weak to allow us to recover the fact that p2 still points
to some number, so we'll no longer be able to dereference it!

The problem is even more compounded when we use abstract predicates,
which are necessary to get at the notion of abstract types. Here,
the inc provider has no way to anticipate what properties P might
be preserved by inc, since P could talk about any property of the
heap.