I started to play with Coq, the interactive theorem prover developed by Inria, a few weeks ago. It is a very powerful tool, yet hard to master. Fortunately, there are some very good readings if you want to learn1. This article is not one of them.

In this article, we will see how to implement strongly-specified list manipulation functions in Coq. Strong specifications are used to ensure some properties on functions’ arguments and return value. It makes Coq type system very expressive. Thus, it is possible to specify in the type of the function pop that the return value is the list passed in argument in which the first element has been removed!

Defining the list type

Thanks to Coq polymorphism and inductive types, it is very simple to define what a list is. Note that to deal with polymorphism, we enclose all our Coq definitions in a Section block.

Is this list empty?

It’s the first question to deal with when manipulating lists. There are some functions that require their arguments not to be empty. It’s the case for the pop function, for instance: it is not possible to remove the first element of a list that does not have any elements in the first place.

When one wants to answer such a question as “Is this list empty?”, he has to keep in mind that there are two ways to do it: by a predicate or by a boolean function. Indeed, Prop and bool are two different worlds that do not mix easily. One solution is to write two definitions and to prove their equivalence. That is ∀ args, predicate args ↔ bool_function args = true.

Another solution is to use the Sumbool type as middleman. The scheme is the following:

In addition to list 'a, Coq has created the sumbool and bool types and empty_b is basically a translation from the first to the second. We could have stopped with empty_dec, but Left and Right are less readable that True and False.

Defining some utility functions

Defining pop

There are several ways to write a function that removes the first element of a list. One is to return nil if the given list was already empty:

It’s better and yet it can still be improved. Indeed, according to its type, pop returns “some list”. As a matter of fact, pop returns “the same list without its first argument”. It is possible to write such precise definition thanks to sigma-types, defined as:

If one tries to call (pop nil), the assert ensures the call fails. Extra information given by the sigma-type have been stripped away. It can be confusing, but the implementation still respects the related property.

Defining push

It is possible to specify push the same way pop has been. The only difference is push accepts lists with no restriction at all. Thus, its definition is a simpler, but it still uses refine to deal with the exist constructor.