The reification of syntax, typing and commands of Coq allow writing a Coq plugin directly inside Coq, without requiring another language like OCaml and an external compilation phase.
In this section, we describe three examples of such plugins: (i) a plugin that adds a constructor to an inductive type, (ii) a re-implementation of Lasson ’s parametricity pluginFootnote 3, and (iii) an implementation of a plugin that provides an extension of CIC—using a syntactic translation—in which it is possible to prove the negation of functional extensionality [8].
5.1 A Plugin to Add a Constructor
Our first example is a toy example to show the methodology of writing plugins in Template-Coq. Given an inductive type
, we want to declare a new inductive type
which corresponds to
plus one more constructor.
For instance, let’s say we have a syntax for lambda calculus:
And that in some part of our development, we want to consider a variation of
with a new constructor, e.g.,
. Then we declare
with the plugin by:
This command has the same effect as declaring the inductive
by hand:
but with the benefit that if
is changed, for instance by adding one new constructor, then
is automatically changed accordingly. We provide other examples in the file
, e.g. with mutual inductives.
We will see that it is fairly easy to define this plugin using Template-Coq. The main function is
which takes an inductive type
(whose type is not necessarily
if it is an inductive family), a name
for the new constructor and the type
of the new constructor, abstracted with respect to the new inductive.
It works in the following way. First the inductive type
is quoted, the obtained term
is expected to be a
constructor otherwise the function fails. Then the declaration of this inductive is obtained by calling
, the constructor is reified too, and an auxiliary function is called to add the constructor to the declaration. After evaluation, the new inductive type is added to the current context with
.
It remains to define the
auxiliary function to complete the definition of the plugin. It takes a
which is the declaration of a block of mutual inductive types and returns a
.
The declaration of the block of mutual inductive types is a record. The field
contains the list of declarations of each inductive of the block. We see that most of the fields of the records are propagated, except for the names which are translated to add some primes and
the list of types of constructors, for which, in the case of the relevant inductive (
is its number), the new constructor is added.
5.2 Parametricity Plugin
We now show how Template-Coq permits to define a parametricity plugin that computes the translation of a term following Reynolds’ parametricity [21, 25]. We follow the already known approaches of parametricity for dependent type theories [7, 15], and provide an alternative to Keller and Lasson’s plugin.
The definition in the unary case is described in Fig. 4. The soundness theorem ensures that, for a term t of type A, \([t]_1\) computes a proof of parametricity of \([t]_0\) in the sense that it has type \([A]_1\, [t]_0\). The definition of the plugin goes in two steps: first the definition of the translation on the syntax of
in Template-Coq and then the instrumentation to connect it with terms of Coq using the
. It can be found in the file
.
The parametricity translation of Fig. 4 is total and syntax directed, the two components of the translation \([\ ]_0\) and \([\ ]_1\) are implemented by two recursive functions
and
.
On Fig. 4, the translation is presented in a named setting, so the introduction of new variables does not change references to existing ones. That’s why, \([\ ]_0\) is the identity. In the De Bruijn setting of Template-Coq, the translation has to take into account the shift induced by the duplication of the context. Therefore, the implementation
of \([\ ]_0\) is not the identity anymore. The argument
of
represents the De Bruijn level from which the variables have been duplicated. There is no need for such an argument in
, the implementation of \([\ ]_1\), because in this function all variables are duplicated.
The parametricity plugin not only has to be defined on terms of CIC but also on additional terms dealing with the global context. In particular, constants are translated using a translation table which records the translations of previously processed constants.
If a constant is not in the translation table we return a dummy
, considered as an error (this could also be handled by an option monad).
We have also implemented the translation of inductives and pattern matching. For instance the translation of the equality type
produces the inductive type:
Then \([\mathtt {eq}]_1\) is given by
and \([\mathtt {eq\_refl}]_1\) by \(\mathtt {eq\_refl^t}\).
Given
and
the translation of the declaration of a block of mutual inductive types is not so hard to get. Indeed, such a declaration mainly consists of the arities of the inductives and the types of constructors; and the one of the translated inductive are produced by translation of the original ones.
In a similar manner, we can translate pattern-matching. Note that the plugin does not support fixpoints and cofixpoints for the moment.
Now, it remains to connect this translation defined on reified syntax
to terms of Coq. For this, we define the new command
in the
.
When
is a definition, the command recovers the body of
(as a
) using
and then translates it and records it in a new definition
. The command returns the translation table
extended by
. In the case
is an inductive type or a constructor then the command does basically the same but extends the translation table with both the inductive and the constructors. If
is an axiom or not a constant the command fails.
Here is an illustration coming from the work of Lasson [16] on the automatic proofs of (\(\omega \)-)groupoid laws using parametricity. We show that all function of type
are identity functions. First we need to record the translations of
and
in a term
of type
.
Then we show that every parametric function on
is pointwise equal to the identity using the predicate
.
Then we define a function
\(p \mapsto p \centerdot p^{\text{-1 }} \centerdot p\) and get its parametricity proof using the plugin.
It is then possible to deduce automatically that \(p \centerdot p^{\text{-1 }} \centerdot p = p\) for all \(p:x=y\).
5.3 Intensional Function Plugin
Our last illustration is a plugin that provides an intensional flavour to functions and thus allows negating functional extensionality (FunExt). This is a simple example of syntactical translation which enriches the logical power of Coq, in the sense that new theorems can be proven (as opposed to the parametricity translation which is conservative over CIC). See [8] for an introduction to syntactical translations and a complete description of the intensional function translation.
Even if the translation is very simple as it just adds a boolean to every function (Fig. 5), this time, it is not fully syntax directed. Indeed the notation for pairs hide some types:
and we can not recover the type
from the source term. There is thus a mismatch between the lambdas which are not fully annotated and the pairs which are.Footnote 4
However we can use the type inference algorithm of Sect. 3 implemented on Template-Coq terms to recover the missing information.
Compared to the parametricity plugin, the translation function has a more complex type as it requires the global and local contexts. However, we can generalize the
command so that it can be used for both the parametricity and the intensional function plugins. The implementation is in the files
and
.
Extending Coq Using Plugins. The intensional translation extends the logical power of Coq as it is possible for instance to negate FunExt. In this perspective, we defined a new command:
which computes the translation
of
, then asks the user to inhabit the type
by generating a proof obligation and then safely adds the axiom
of type
to the current context. By safely, we mean that the correction of the translation ensures that no inconsistencies are introduced.
For instance, here is how to negate FunExt. We use for that two pairs
and
in the interpretation of functions from
to
, which are extensionally both the identity, but differ intensionally on their boolean.
where
and
are special versions of the corresponding
and
tactics of Coq to deal with extra booleans appearing in the translated terms. After this command, the axiom
belongs to the environment, as if it where added with the
command. But as we have inhabited the translation of its type, the correctness of the translation ensures that no inconsistency were introduced.
Note that we could also define another translation, e.g. the setoid translation, in which FunExt is inhabited. This is not contradictory as the two translations induce two different logical extensions of Coq, which can not be combined.