To see why inversion
isn't able to solve this goal on its own, we need to understand what it is doing in more detail.
When you invert a hypothesis of (co)inductive type, what Coq does, roughly speaking, is to extract as much information as it can from it using only pattern matching (remember that Coq tactics are always producing proof terms under the hood). Thus, when proving 1 <> 0
by inversion, Coq will produce a term that looks like this:
Definition one_neq_zero (p : 1 = 0) : False :=
match p in _ = n return match n with
| 0 => False
| _ => True
end
with
| eq_refl => I (* "I" is the only constructor of the True proposition *)
end.
The return
annotation on the match
statement is crucial for this to work. What is happening here is essentially the following:
- We need to match on the equality proof
p
to use it.
- In order to be able to talk about the right-hand side of that equation when pattern-matching on its proof, we must add a return annotation to our match.
- Unfortunately, this return annotation can't mention 0 directly. Instead, it needs to work for a generic
n
, even though we know that that element is actually 0. This is just because of the way pattern matching works in Coq.
- On the return annotation, we play a "trick" on Coq: we say that we will return
False
on the case that we actually care about (i.e., n = 0
), but say that we'll return something else on the other branches.
- To type-check the
match
, each branch must return something of the type that appears on the return
clause, but after substituting the actual values of the variables that were bound on the in
clause.
- In our case, there is only one constructor for the equality type,
eq_refl
. Here, we know that n = 1
. Substituting 1 for n
in our return type, we obtain True
, so we must return something of type True
, which we do.
- Finally, since the right-hand side of
p
is 0, Coq understands that the type of the whole match is False
, so the whole definition type-checks.
The last step only works because 0 is a constructor, so Coq is able to simplify the match on the return type to realize that we are returning the right thing. This is what fails when trying to invert S n = n
: since n
is a variable, the entire match can't be simplified.
We could try to flip the equality and invert n = S n
instead, so that Coq would be able to simplify the return type a little bit. Unfortunately, this wouldn't work either, and for a similar reason. For instance, if you tried to annotate the match with in _ = m return match m with 0 => True | _ => False end
, inside the eq_refl
we would have to return something of type match n with 0 => True | _ => False end
, which we can't.
Finally I should mention that the unification algorithm inside Coq cannot be used "negatively" like you alluded to, because the theory only defines the things that are provable, and not which things are refutable. In particular, when we prove a negated proposition such as S n <> n
, the type checker is always testing whether certain unification problems have a solution, as opposed to testing whether they have no solution. For instance, assuming that n = m
is a perfectly fine thing to do, and doesn't result in any contradictions, even though n
and m
are not unifiable. Also notice that, if nat
were declared as a co-inductive type instead, S n = n
is not a contradictory hypothesis, and the difference between the two is just that on this case we wouldn't be able to do induction on n
.
inversion
tactic either performs case analysis on a predicate or simplifies an equation between constructors. It doesn't apply to your hypothesis. Also, unification is probably stronger than Coq's logic. – Perce Strop