Search tutorial for Coq
Summary
Table of content
- 1. Searching for lemmas
- 1.1 Basic Search by name and type
- 1.1.1 Exercise: find parity results on nat
- 1.2 Search and notations
- 1.2.1 Exercise: sum and products on nat and Type
- 1.3 Searching using metavariables
- 1.3.1 Exercise: find lemmas with metavariables
- 1.4 Filtering on hypotheses (parameters) or conclusions (return type)
- 1.4.1 Exercise: finding divisibility results
- 1.1 Basic Search by name and type
- 2. Advanced Search options
- 2.1 Disambiguating strings in Search queries
- 2.2 Filtering results by logical kind and disjunctions of criteria.
- 2.3 Searching inside or outside a specific module
Prerequisites
- For the first part, it suffices to have some basic experience of Coq: the user should know intuitively what is a Coq function and a Coq lemma or theorem. Scope notations are mentioned briefly but this should not be too much of an issue.
- For the 2.3 part, some basic experience of Coq modules should be enough.
Warning
1. Searching for lemmas
1.1 Basic Search by name and type
- a given constant, eg Nat.add, or nat
- a given string to be part of the identifier (the name)
Search all lemmas or operations where Nat.add occurs:
Search all lemmas or operations whose name contains "add":
Search "add".
In practice, we often want to search for lemmas satisfying multiple
criteria, e.g. search for all lemmas whose name contain "add" and whose
type contains Nat.mul.
To do so, it suffices to give these criteria to Search, it acts as a
conjunction:
We can give as many criteria as we want. Below we search for constants
which satisfy:
- has "mul" in the name and
- has Nat.add in the type and
- has Nat.odd in the type
We can also filter out results satisfying a criterion with by prepending
it with a minus - symbol.
Say we're looking for a lemma stating that the sum of an even number and an
odd number is odd:
This is already good, but we're not interested in lemmas whose name
contain "mul", so we can filter them out to get clearer results by adding
-"mul":
1.1.1 Exercise: find parity results on nat
- Nat.Odd and Nat.Even (in Prop) are the usual existentially quantified definitions;
- Nat.odd and Nat.even (in bool) are functions which compute.
Print Nat.Odd.
Print Nat.Even.
Print Nat.odd.
Print Nat.even.
Compute Nat.even 42.
Compute Nat.odd 42.
Print Nat.Even.
Print Nat.odd.
Print Nat.even.
Compute Nat.even 42.
Compute Nat.odd 42.
Find all lemmas, with a precise enough search, which
1. state an equivalence between being Even and being even;
2. state that the successor (function S) of an even number is odd;
3. state that the product of an Even number by any other number is Even;
4. state that a number is Even or (predicate or) Odd and
5. state that it is true that a number is even or (boolean operation
orb) odd.
(* Beginning of solution *)
(* 1. *) Search Nat.Even Nat.even -Nat.add.
(* 2. *) Search Nat.even Nat.odd S.
(* 3. *) Search Nat.mul Nat.Even -Nat.add -Nat.Odd.
(* 4. *) Search or Nat.Even Nat.Odd -Nat.add -Nat.mul.
(* 5. *) Search orb Nat.even Nat.odd -Nat.add -Nat.mul.
(* End of solution *)
1.2 Search and notations
Indeed, Coq uses notations to display such results, and these are exactly
what the user may expect. In fact, we often forget the names of operations
such as Nat.add.
We can search using notations in the following way:
Notice the wildcards _ around the operators. We call (_ × _), (_ + _)
and (_ / _) Search patterns.
We will soon see how to write more sophisticated such patterns.
Alternatively, we can use strings (more about this later):
Search "+" "*" "/".
If we're interested in the name of the function or predicate associated to
a given notation, we can use the Locate command which shows how Coq
currently interprets a given notation:
Locate "_ + _".
That's unexpected, we actually have 2 different meanings for "+"!
As a side note, the "+" operator between types is the disjoint union. It's
fine if you don't know what this means, our only concern here is that it
exists.
Now, how does Coq choose between them? What is the current interpretation
of "+"?.
Check 2 + 3. (* this looks ok with natural numbers *)
Fail Check nat + bool. (* this fails with types *)
Actually, we could have guessed it with the output of Locate: the default
interpretation for "+", without curly braces on the sides is
Init.Nat.add.
Another way to get this information is to use the About command:
About "_ + _".
We have seen that Coq allows to use the same notation in different context.
In fact, there are many other uses of "+" in the standard library, it could
be the addition between rational numbers, real numbers, ...
The interpretation context for a notation is called a notation scope.
When a notation is ambiguous, because it belongs to more than one scope,
its default interpretation is the one in the last opened scope.
We can display open scopes with the Print Visibility command, note that
the more recently opened scopes (which have a higher priority) appear last.
Print Visibility.
Here, the last scope is nat_scope, we see all the notations associated
to it: order relations (≤, <, ...) and operations (+, ×, ...).
If we want to use + with its interpretation in type_scope, without
changing the opened scope order, we can do so with a scope delimiting key:
Scope delimiting keys are abbreviations of scope names, usually obtained by
removing the _scope suffix.
One can see the delimiting key associated to a scope name with the
Print Scope command.
Print Scope nat_scope.
Check (2 + 3)%nat. (* works regardless of the scopes order *)
Print Scope type_scope.
Check (2 + 3)%nat. (* works regardless of the scopes order *)
Print Scope type_scope.
We can (and should) use delimiting keys in Search queries with patterns
and notation strings:
For instance, if we want to know lemmas about product types, we can write a
type key:
The following query do not depend on the scopes order:
We have used Search patterns with binary operators, similarly, we can
search about a unary prefix operator, such as ¬ (the negation), with:
In that case it was not necessary to write explicitly the type key, since
the ¬ notation does not belong to scope nat_scope.
In fact, the ¬ currently has only one interpretation.
Locate "~ _".
Now, let's search for lemmas about distributivity of multiplication over
addition on nat.
To do so, we can first search all lemmas where the type nat and the
operators (_ * _) and (_ + _) occur.
Since our current scope is nat, the first search item is useless.
We can ask for distributivity lemmas, these contain the string "distr" in
their names.
1.2.1 Exercise: sum and products on nat and Type
(* Beginning of solution *)
(* 1. *) Search (_ × _)%type nat.
(* 2. *) Search nat (_ + _)%type.
(* 3. *) Search (_ + _ ≤ _ × _)%nat.
(* End of solution *)
1.3 Searching using metavariables
What about the commutativity of Nat.add?
The list is somewhat manageable (and, by sheer luck, Nat.add_comm appears
first), but we can be much more precise with meta-variables.
We can even search for all commutativity lemmas:
If we want to exclude the bool commutativity lemmas, we can, as before
add the restriction that we want nat to be involved:
Another possibility is to exclude any results mentioning bool:
We can also opt to remove only results with "orb" it its name:
Or results where the (_ || _) operator appears (notice the scope key, since
bool_scope is not currently opened):
1.3.1 Exercise: find lemmas with metavariables
(* Beginning of solution *)
(* 1. *) Search (?a ≤ ?a + _).
(* 2. *) Search (?op1 ?a (?op2 ?b ?c) = ?op2 (?op1 ?a ?b) (?op1 ?a ?c)).
(* 3. *) Search ((Nat.divide ?a ?b) → ?a ≤ ?b).
(* End of solution *)
(* 1. *) Search (?a ≤ ?a + _).
(* 2. *) Search (?op1 ?a (?op2 ?b ?c) = ?op2 (?op1 ?a ?b) (?op1 ?a ?c)).
(* 3. *) Search ((Nat.divide ?a ?b) → ?a ≤ ?b).
(* End of solution *)
1.4 Filtering on hypotheses (parameters) or conclusions (return type)
Of course, the hyp: specifier also exists to Search for a pattern or
notation occurring in one of the hypotheses (a.k.a. parameters):
We can be even more precise and specify that our pattern (or notation)
should appear at the head of the conclusion (or an hypothesis), that is,
the whole term should match our pattern, instead of a subterm.
As an example, let us Search for lemmas about conjunctions and
disjunctions.
We want lemmas whose conclusion is a conjunction.
We exclude nat in order to get only purely logical properties.
Notice that, in Decidable.not_iff and bool_choice, the conclusion
contains a conjunction but is not a conjunction. We can exclude them
with the headconcl specifier:
The equivalence Decidable.not_or_iff has also disappeared, because
it is actually treated as a lemma having a single conclusion of the form
(_ ↔ _), hence not of the form (_ ∧ _).
We can list all lemmas about addition with an equivalence as a conclusion:
Search "+" headconcl:"<->".
and all lemmas having an equivalence as a hypothesis.
Search headhyp:"<->".
Finally the head keyword is just a shorthand for headconcl or
headhyp:
Search head:"<->".
1.4.1 Exercise: finding divisibility results
(* Beginning of solution *)
Search concl:(_ ≤ _) hyp:Nat.divide.
Search hyp:(Nat.divide _ (_ × _)).
Search headconcl:Nat.divide.
(* End of solution *)
Search concl:(_ ≤ _) hyp:Nat.divide.
Search hyp:(Nat.divide _ (_ × _)).
Search headconcl:Nat.divide.
(* End of solution *)
2. Advanced Search options
2.1 Disambiguating strings in Search queries
Search "comm".
or, search for constants whose type contain a notation, such as:
Search "||".
Search "+".
Search "+".
So, what really happens here?
First, Coq makes a different between identifier (name) characters and
other characters.
The characters '|' and '+' cannot be used in constant names, so Coq guesses
it should interpret "||" and "+" as notations to appear in the types, not as
substrings of names.
What happens when a notation uses only identifier characters?
This is the case for the mod operator:
About "mod".
Searching for "mod" lists constants whose identifiers contain "mod", as
is indicated in the divmod operation here:
Search "mod".
To Search for constants where the notation mod appears in the type, we
have two solutions: either we enclose the string with single quotes:
Search "'mod'".
or we give an explicit scope key (hence it must be a notation):
Search "mod"%nat.
Of course, we can still use a pattern instead of a string:
2.2 Filtering results by logical kind and disjunctions of criteria.
Search "add" 0 is:Lemma.
Same for constants defined with the Theorem keyword:
Search "add" 0 is:Theorem.
Now, if we want both Lemmas and Theorems we can use the general
"or" construct in Search queries:
Search "add" 0 [is:Theorem | is:Lemma].
The following lists lemmas and theorems about Nat.add or Nat.mul
containing the strings "comm" or "assoc":
The interested reader can refer to
the reference manual(https://coq.inria.fr/doc/V8.19.0/refman/proof-engine/vernacular-commands.html?highlight=searchsearch-by-keyword)
for the list of all logical kinds which can appear after [is:].
Finally it is possible to further restrict the Search results inside or
outside a specific Module.
2.3 Searching inside or outside a specific module
The in keyword is a shorthand for inside:
The outside keyword on the contrary excludes results from a specific
Module.
Search "+" outside Nat.
Now, these last Searches exhibit a subtlety related to the Module
System. Actually, there are two modules named Nat in our environment.
The first to appear is the last to be Required. In our case, its full
name is Coq.Arith.PeanoNat.Nat:
About Nat.
Print Module Nat.
Print Module Nat.
But this Nat module shadows the Nat module which already exists in
Init (actually, as a side note, the Coq.Init.Nat module is Included
in Coq.Arith.PeanoNat.Nat, but this is not relevant in this tutorial).
If one wants to Search for all lemmas in the Nat module taken from
Init, it suffices to qualify it more to disambiguate it:
Search _ in Init.Nat.
We can even shadow PeanoNat.Nat module in this file:
Module Nat.
Lemma foo : 21 + 21 = 42. Proof. reflexivity. Qed.
End Nat.
Search _ in Nat.
Search "f" "o" in PeanoNat.Nat.
Lemma foo : 21 + 21 = 42. Proof. reflexivity. Qed.
End Nat.
Search _ in Nat.
Search "f" "o" in PeanoNat.Nat.
The take-home message here is that modules are often used to provide
namespace facilities, so when using inside and outside, one should
often qualify them more.