We present here the new Program tactic commands, used to build certified Coq programs, elaborating them from their algorithmic skeleton and a rich specification [118]. It can be sought of as a dual of extraction (see Chapter 18). The goal of Program is to program as in a regular functional programming language whilst using as rich a specification as desired and proving that the code meets the specification using the whole Coq proof apparatus. This is done using a technique originating from the “Predicate subtyping” mechanism of PVS[115], which generates type-checking conditions while typing a term constrained to a particular type. Here we insert existential variables in the term, which must be filled with proofs to get a complete Coq term. Program replaces the Program tactic by Catherine Parent [105] which had a similar goal but is no longer maintained.
The languages available as input are currently restricted to Coq’s term language, but may be extended to Objective Caml, Haskell and others in the future. We use the same syntax as Coq and permit to use implicit arguments and the existing coercion mechanism. Input terms and types are typed in an extended system (Russell) and interpreted into Coq terms. The interpretation process may produce some proof obligations which need to be resolved to create the final term.
The main difference from Coq is that an object in a type T : Set can be considered as an object of type { x : T | P} for any wellformed P : Prop. If we go from T to the subset of T verifying property P, we must prove that the object under consideration verifies it. Russell will generate an obligation for every such coercion. In the other direction, Russell will automatically insert a projection.
Another distinction is the treatment of pattern-matching. Apart from the following differences, it is equivalent to the standard match operation (see Section 4.5.4).
will be first rewrote to:
This permits to get the proper equalities in the context of proof obligations inside clauses, without which reasoning is very limited.
If you do specify a return or in clause the typechecker will fall back directly to Coq’s usual typing of dependent pattern-matching.
The next two commands are similar to their standard counterparts Definition (see Section 1.3.2) and Fixpoint (see Section 1.3.4) in that they define constants. However, they may require the user to prove some goals to construct the final definitions.
This command types the value term in Russell and generate proof obligations. Once solved using the commands shown below, it binds the final Coq term to the name ident in the environment.
Error messages:
Variants:
Error messages:
See also: Sections 6.2.4, 6.2.5, 8.5.5
The structural fixpoint operator behaves just like the one of Coq (see Section 1.3.4), except it may also generate obligations. It works with mutually recursive definitions too.
Here we have one obligation for each branch (branches for 0
and (S 0)
are
automatically generated by the pattern-matching compilation algorithm):
You can use a well-founded order or a measure as termination orders using the syntax:
Caution: When defining structurally recursive functions, the generated obligations should have the prototype of the currently defined functional in their context. In this case, the obligations should be transparent (e.g. using Defined or Save) so that the guardedness condition on recursive calls can be checked by the kernel’s type-checker. There is an optimization in the generation of obligations which gets rid of the hypothesis corresponding to the functionnal when it is not necessary, so that the obligation can be declared opaque (e.g. using Qed). However, as soon as it appears in the context, the proof of the obligation is required to be declared transparent.
No such problems arise when using measures or well-founded recursion.
The Russell language can also be used to type statements of logical properties. It will currently fail if the traduction to Coq generates obligations though it can be useful to insert automatic coercions. In the former case, one can first define the lemma’s statement using Program Definition and use it as the goal afterwards.
The Program prefix can similarly be used as a prefix for Variable, Hypothesis, Axiom etc...
The following commands are available to manipulate obligations. The optional identifier is used when multiple functions have unsolved obligations (e.g. when defining mutually recursive blocks). The optional tactic is replaced by the default one if not specified.
The module Coq.subtac.Utils defines the default tactic for solving obligations called subtac_simpl. Importing it also adds some useful notations, as documented in the file itself.