when is the `:` (colon) in necessary in ssreflect/Coq? - coq

I am trying to understand the exact meaning of the : (colon) in Coq/ssreflect proofs in terms of non-ssreflect Coq.
I read that it has something to do with moving things to the goal (like generalize??) and is the opposite of =>, which move things to the hypotheses. However, I often find it confusing because proofs work either way with or without the :. Below is an example from a tutorial:
Lemma tmirror_leaf2 t : tmirror (tmirror t) = Leaf -> t = Leaf.
Proof.
move=> e.
by apply: (tmirror_leaf (tmirror_leaf e)).
Qed.
where,
tmirror_leaf
: forall t, tmirror t = Leaf -> t = Leaf
is a lemma that says if the mirror of a tree is a leaf, then the tree is a leaf.
I don't understand why we need the : here and not merely do the Coq apply. In fact, if I remove the :, it works just fine. Why does it make a difference?

Indeed, apply: H1 ... Hn is to all effects equivalent to move: H1 .. Hn; apply. A more interesting use of apply is apply/H and its variations, which can interpret views.

I think I found the answer while reading the SSReflect documentation. Essentially, ssr has redefined tactics like apply such that it operates on the first variable of the goal instead of something in the context. That's why the : is used in apply: XX. in the ssr way (which is equivalent to move: XX; apply.), and it also works if : is omitted as that is the traditional Coq way.
Quoting the documentation:
Furthermore, SSReflect redefines the basic Coq tactics case, elim, and
apply so that they can take better advantage of ':' and '=>'. These
Coq tactics require an argument from the context but operate on the
goal. Their SSReflect counterparts use the first variable or constant
of the goal instead, so they are "purely deductive":
they do not use or change the proof context. There is no loss since
`:' can readily be used to supply the required variable.

Related

Finding rewrite rules

I have a hard time finding the available rewrite rules for my situation. As I don't want to bother you with each rewrite question, I was wondering do you have some tips for finding suitable rewrite rules?
Do you have any tips on how to solve and or search for rewriting the following example:
1 subgoal
H: P
H0: Q
__________
R
And say I have Lemma Join: P /\ Q = R
In order to do this rewrite, I suppose I need to get H and H0 first rewritten into P /\ Q.
So how would you solve or find the rewrite rules for such a case?
Another example
H: a <= b
____________
b < a
I am confident there should exists some commutativity rewrite rule for this, but how can I best find this rule?
Many thanks in advance!
First a tip so you don't run into this problem later: Don't confuse equality of types for logical equivalence. What you usually mean in your first example above is that P/\Q <-> R, not that the type P/\Q is definitionally the same type as R.
With regards to your question about finding lemmas in the library; yes, it is very important to be able to find things there. Coq's Search command lets you find all (Required) lemmas that contain a certain pattern somewhere in it, or some particular string. The latter is useful because the library tends to have a somewhat predicable naming scheme, for instance the names for lemmas about decidability often contains then string "dec", commutativity lemmas often are called something with "comm" etc.
Try for example to search for decidability lemmas about integers, i.e. lemmas that have the term Z somewhere inside them, and the name contains the string "dec".
Require Import ZArith.
Search "dec" Z.
Back to your question; in your case you want to find a lemma that ends with "something and something", so you can use the pattern ( _ /\ _ )
Search ( _ /\ _ ).
However, you get awfully many hits, because many lemmas ends with "something and something".
In your particular case, you perhaps want to narrow the search to
Search (?a -> ?b -> ?a /\ ?b).
but be careful when you are using pattern variables, because perhaps the lemma you was looking for had the arguments in the other order.
In this particular case you found the lemma
conj: forall [A B : Prop], A -> B -> A /\ B
which is not really a lemma but the actual constructor for the inductive type. It is just a function. And remember, every theorem/lemma etc. in type theory is "just a function". Even rewriting is just function application.
Anyway, take seriously the task of learning to find lemmas, and to read the output from Search. It will help you a lot.
Btw, the pattern matching syntax is like the term syntax but with holes or variables, which you will also use when you are writing Ltac tactics, so it is useful to know for many reasons.

Show theorem definition in Coq

I'd like to view the definition of a Standard Library theorem which I found through Search. I think seeing the definition will help me complete a similar theorem.
Doing Print Rdiv_lt_0_compat. yields:
Rdiv_lt_0_compat =
fun (a b : R) (H : (0 < a)%R) (H0 : (0 < b)%R) =>
Rmult_lt_0_compat a (/ b) H (Rinv_0_lt_compat b H0)
: forall a b : R, (0 < a)%R -> (0 < b)%R -> (0 < a / b)%R
Argument scopes are [R_scope R_scope _ _]
Setting Set Printing All. doesn't help. There's nothing extra available in the docs page.
The whole Coq system is based on the idea Proofs are programs, logical formulas are types. When you consider a theorem, it is a proof (a program) and its statement is a logical formula (the type of a program). In the very
first years of Coq, there was no tactic language, every proof was defined using the same keywords as when defining a program.
After a few years, it was recognized that writing the programs entirely by hand was long and tiresome, so a tactic language was invented to explain how to construct the proof-programs in a shorter and less difficult way. But what is recorded and eventually checked are still the programs that you see using Print.
When building a proof-program, the tactic intros constructs anonymous function expressions (also known as lambdas, usually written with the keyword fun, and apply constructs an application of a function to a certain number arguments, which apply infers or leaves to the user as goals. The tactics induction and rewrite are similar, but they apply theorems that are not
given by the user. The tactic destruct essentially produces a piece of programs that is a pattern-matching construct.
With Rdiv_lt_0_compat, you are lucky that the proof built by the tactic is quite short. Often, proofs written using tactics produce programs that are much longer.
If instead of the program, you want to see the sequence of tactics that generated it, you need to find it in the sources of the system, because this
is not kept in the memory of the proof assistant. Here are the clues.
Require Import Reals.
Locate Rdiv_lt_0_compat.
the answer is Constant Coq.Reals.RIneq.Rdiv_lt_0_compat
This sequence of names indicates the hierarchy of modules in which the theorem is kept. The first name Coq expresses that this theorem is in the Coq sources, essentially in directory ...theories/, the second name Reals, indicates that you should look in tge sub directory ...theories/Reals.
The fourth name should not be used as a directory name, but as file name. So you should look in the file RIneq.v
So go an look in https://github.com/coq/coq/tree/v8.12/theories/Reals/RIneq.v and you will probably find the script fragment that was used to generate your theorem (for the 8.12 version of Coq). I just checked, the theorem appears at line https://github.com/coq/coq/blob/c95bd4cf015a3084a8bddf6d3640458c9c25b455/theories/Reals/RIneq.v#L2106
The sequence of names provided by Locate is not a sure way to find the file where the script for a theorem is stored. The correspondence between the long name and the file path is broken when the theorem is defined using modules and functor instantiation. In that case, you have to rely on stronger knowledge of how the Coq system works.

What is the difference between definitions and theorems?

In the current version of the Software Foundations, the relevant explanation is formulated with intentional leeway.
First, we've used the keyword Theorem instead of Example.
This difference is mostly a matter of style; the keywords
Example and Theorem (and a few others, including Lemma,
Fact, and Remark) mean pretty much the same thing to Coq.
(I put the wording used to introduce uncertainty in bold.)
In an earlier version of, evidently, the same writing, it was put strongly:
The form of this theorem and proof are almost exactly the same as the examples above: the only differences are that we've added the quantifier ∀ n:nat and that we've used the keyword Theorem instead of Example. Indeed, the latter difference is purely a matter of style; the keywords Example and Theorem (and a few others, including Lemma, Fact, and Remark) mean exactly the same thing to Coq.
(I put words introducing strength in bold.)
Looking at the official documentation, all of these words, and more, belong to the same grammatical category «assertion keyword»:
assertion_keyword ::= Theorem | Lemma
Remark | Fact
Corollary | Property | Proposition
Definition | Example
There are further two sections explaining how these keywords work: «Definitions» and «Assertions and proofs». In short:
Definitions extend the environment with associations of names to terms.
— And:
An assertion states a proposition (or a type) of which the proof (or an inhabitant of the type) is interactively built using tactics.
But, as far as I see, a theorem also extends the environment, and an example can be built with tactics. So, I am not seeing where these things are different. But the Software Foundation is a clever book. If they chose to be uncertain, there must be a reason?
As of today the differences are minimal, and could be summarized as:
Theorem/Example/Definition/... do produce different entries in the documentation; this is the most relevant difference.
Some forms such as Theorem don't support the syntactical form Theorem foo := nat. for non-interactive mode, which is supported by Definition for example.
Another non-trivial difference is whether you use the interactive mode or not. That is to say:
Definition foo : Type := nat.
and
Definition foo : Type. Proof. apply nat. Qed.
will take slightly different code paths due to the second creating an interactive proof; the codepath when the definition gets sent to the kernel is a bit different, but Coq 8.12 should unify them for all practical purposes, just marking the second proof as opaque.
One difference I've seen is that Definitions can give an immediate definition, whereas Lemma and Theorem can't.
Definition one := 1.
Lemma two := 2. (* syntax error *)
Theorem three := 3. (* syntax error *)
Out of the rest, only Example works.
Remark four := 4. (* syntax error *)
Fact five := 5. (* syntax error *)
Corollary six := 6. (* syntax error *)
Property seven := 7. (* syntax error *)
Proposition eight := 8. (* syntax error *)
Example nine := 9. (* works! *)

Coq: viewing proof term during proof script writing

So, I've got a proof that looks like this:
induction t; intros; inversion H ; crush.
It solves all my goals, but when I do Qed, I get the following error:
Cannot guess decreasing argument of fix.
So somewhere in the generated proof term, there's non-well-founded recursion. The problem is, I have no idea where.
Is there a way to debug this kind of error, or to see the (possibly non halting) proof term that the tactics script generates?
You can use Show Proof. to view the proof term so far.
Another command that can help with seeing where the recursion went wrong is Guarded., which runs the termination checker on the proof term so far. You'll need to break apart the tactic script into independent sentences to use it, though. Here's an example:
Fixpoint f (n:nat) : nat.
Proof.
apply plus.
exact (f n).
Guarded.
(* fails with:
Error:
Recursive definition of f is ill-formed.
...
*)
Defined.
You can use the Show Proof. command inside proof mode to print the proof term produced so far.
In addition to the other excellent answers, I also want to point out that using induction inside an interactive-mode Fixpoint is usually a mistake, because you're recursing twice. Writing fixpoints in interactive mode is often tricky because most automation tools will happily make a recursive call at every possible opportunity, even when it would be ill-founded.
I would advise to use Definition instead of Fixpoint and use induction in the proof script. This invokes the explicit recursor, which allows for much better control of automation. The disadvantage is decreased flexibility since fixpoints have fewer restrictions than recursors - but as we've seen, that is both a blessing and a curse.

Definition vs Notation for constants

I'm extending an existing project (Featherweight Java formalization),
and there are a number of constants, such as:
Notation env := (list (var * typ)).
What would change if I used Definition instead:
Definition env := (list (var * typ)).
Why did the author use Notation here?
Whenever you try to apply or rewrite with a lemma, there's a component in Coq called the unifier that tries to find out how to instantiate your lemma so that it can work with the situation at hand (and checking that it indeed applies there). The behavior of this unifier is a bit different depending on whether you use notations or definitions.
Notations are invisible to Coq's theory: they only affect the parsing and printing behavior of the system. In particular, the unifier doesn't need to explicitly unfold a notation when analyzing a term. Definitions, on the other hand, must be explicitly unfolded by the unifier. The problem is that the unifier works heuristically, and cannot tell with 100% certainty when some definition must be unfolded or not. As a consequence, we often find ourselves with a goal that mentions a definition that the unifier doesn't unfold by itself, preventing us from applying a lemma or rewriting, and having to manually add a call to unfold ourselves to get it to work.
Thus, notations can be used as a hack to help the unifier understand what an abbreviation means without manual unfolding steps.