Related Threads

Contents

I have to confess something. In the published codebase of SpecCert lies a shameful secret. It takes the form of a set of axioms which are not required. I thought they were when I wrote them, but it was before I heard about “generalized rewriting,” setoids and morphisms.

Now, I know the truth. I will have to update SpecCert eventually. But, in the meantime, let me try to explain how it is possible to rewrite a term in a proof using a ad-hoc equivalence relation and, when necessary, a proper morphism. Be aware, however, that I may not be completely familiar with the underlying mathematics. I will try not to say anything wrong, but if I do, please! Feel free to tell me so.

Gate: Our Case Study

Now, why would anyone want such a thing as “generalized rewriting” when the rewrite tactic works just fine.

The thing is: it does not in some cases. To illustrate my statement, we will consider the following definition of a gate in Coq:

According to this definition, a gate can be either open or closed. It can also be locked, but if it is, it cannot be open at the same time. To express this constrain, we embed the appropriate proposition inside the Record. By doing so, we know for sure that we will never meet an ill-formed Gate instance. The Coq engine will prevent it, because to construct a gate, one will have to prove the lock_is_close predicate holds.

The Program keyword makes it easy to work with embedded proofs. For instance, defining the ”open gate” is as easy as:

Gate Equality

What does it mean for two gates to be equal? Intuitively, we know they have to share the same states (open and lock is our case).

Leibniz Equality Is Too Strong

When you write something like a = b in Coq, the = refers to the eq function and this function relies on what is called the Leibniz Equality. You can have a look at the Coq documentation if you want more detail, but the main idea is pretty simple.

x and y are equal iff every property on A which is true of x is also true of y

As for myself, when I first started to write some Coq code, the Leibniz Equality was not really something I cared about and I tried to prove something like this:

Equivalence Relations and Rewriting

So here we are, with our ad-hoc definition of gate equivalence. We can use symmetry, reflexivity and transitivity along with gate_eq and it works fine because we have told Coq gate_eq is indeed an equivalence relation for Gate.

Can we do better? Can we actually use rewrite to replace an occurrence of g by an occurrence of g’ as long as we can prove that gate_eq g g’? The answer is “yes”, but it will not come for free.

What Coq is trying to tell us here —in a very poor manner, I’d say— is actually pretty simple. It cannot replace g by g’ because it does not know if two equivalent gates actually give the same result when passed as the argument of try_open. This is actually what we want to prove, so we cannot use rewrite just yet, because it needs this result so it can do its magic. Chicken and egg problem.

In other words, we are making the same mistake as before: not telling Coq what it cannot guess by itself.

The rewrite tactic works out of the box with the Coq equality (eq, or most likely =) because of the Leibniz Equality.

x and y are equal iff every property on A which is true of x is also true of y

This is a pretty strong property, and one a lot of equivalence relations do not have. Want an example? Consider the relation R over A such that forall x and y, R x y holds true. Such relation is reflexive, symmetric and reflexive. Yet, there is very little chance that given a function f : A -> B and R’ an equivalence relation over B, R x y -> R' (f x) (f y). Only if we have this property, we would know that we could rewrite f x by f y, e.g. in R' z (f x). Indeed, by transitivity of R’, we can deduce R' z (f y) from R' z (f x) and R (f x) (f y).

If R x y -> R' (f x) (f y), then f is a morphism because it preserves an equivalence relation. In our previous case, A is Gate, R is gate_eq, f is try_open and therefore B is Gate and R’ is gate_eq. To make Coq aware that try_open is a morphism, we can use the following syntax:

This notation is actually more generic because you can deal with functions that take more than one argument. Hence, given g : A -> B -> C -> D, R (respectively R’, R’’ and R’’’) an equivalent relation of A (respectively B, C and D), we can prove f is a morphism as follows:

We did it! We actually rewrite g as g’, even if we weren’t able to prove g
= g’.

Rewriting Unary Relations

There is one last thing to cover. How to rewrite R g into R g’ when we have proven gate_eq g g’? By declaring a new morphism, of course! Except the output relation is not either gate_eq or R. What we really want, indeed, is the following: gate_eq g g’ -> R g -> R g’. The Coq Library defines R g -> R g’ as impl in Coq.Program.Basics, so we can do as follows:

Equality in SpecCert

Do not do what follows, use equivalence relation and morphisms instead.

Maybe you are curious and want to know “how did I manage to deal with equality in SpecCert without all this setoids and morphisms dark magic?”. It is pretty simple actually. I’ve defined the Eq typeclass, inspired by the Haskell typeclass: