Logical Foundations

9. IndProp: Inductively Defined Propositions🔗

Note to developers (before next release)

This chapter needs more (and better!) quizzes

import LF.Logic
import LF.CustomTactics
Note to developers (Mike Hicks @mwhicks1)

It would be convenient to declare the variables below so that inline prose throughout this chapter can use n, n', m, m', k, α, x, y, l, l₁, l₂, and l₃ without repeating their type annotations, but the same problem described in Logic applies: an unused variable is silently added to the local context in basically every proof from here on, even when the theorem never mentions it, which makes theorem hover-overs in the HTML book unusable. Until we have a way to declare variables visible only for inline prose (rather than for every lean block), we leave this commented out:

-- variable
--   (n n' m m' k : Nat)
--   (α : Type)
--   (x y : α)
--   (l l₁ l₂ l₃ : List α)

9.1. Inductively Defined Propositions🔗

In the Logic chapter, we looked at several ways of writing propositions, including conjunction, disjunction, and existential quantification.

In this chapter, we bring yet another new tool into the mix: inductively defined propositions.

To begin, some examples...

9.1.1. Example: The Collatz Conjecture🔗

The Collatz Conjecture is a famous open problem in number theory.

Its statement is quite simple. First, we define a function collatzStep on numbers as follows:

def div2 (n : Nat) : Nat := match n with | 0 => 0 | 1 => 0 | n' + 2 => div2 n' + 1 def collatzStep (n : Nat) : Nat := bif n.even then div2 n else (3 * n) + 1

Next, we look at what happens when we repeatedly apply collatzStep to some given starting number. For example, collatzStep 12 is 6, and collatzStep 6 is 3, so by repeatedly applying collatzStep we get the sequence 12, 6, 3, 10, 5, 16, 8, 4, 2, 1.

Similarly, if we start with 19, we get the longer sequence 19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1.

Both of these sequences eventually reach 1. The question posed by Collatz was: Is the sequence starting from any positive natural number guaranteed to reach 1 eventually?

To formalize this question in Lean, we might try to define a recursive function that calculates the total number of steps that it takes for such a sequence to reach 1. You can write this definition in a standard programming language, but it is rejected by Lean's termination checker, since the argument to the recursive call, collatzStep n, is not "obviously smaller" than n.

def fail to show termination for reaches1In with errors failed to infer structural recursion: Cannot use parameter n: failed to eliminate recursive application reaches1In (collatzStep n) failed to prove termination, possible solutions: - Use `have`-expressions to prove the remaining goals - Use `termination_by` to specify a different well-founded relation - Use `decreasing_by` to specify your own tactic for discharging this kind of goal n:Nat⊢ collatzStep n < nreaches1In (n : Nat) : Nat := bif n == 1 then 0 else 1 + reaches1In (collatzStep n)
fail to show termination for
  reaches1In
with errors
failed to infer structural recursion:
Cannot use parameter n:
  failed to eliminate recursive application
    reaches1In (collatzStep n)


failed to prove termination, possible solutions:
  - Use `have`-expressions to prove the remaining goals
  - Use `termination_by` to specify a different well-founded relation
  - Use `decreasing_by` to specify your own tactic for discharging this kind of goal
n:Nat⊢ collatzStep n < n

Indeed, this isn't just a pointless limitation: functions in Lean are required to be total, to ensure logical consistency.

Moreover, we can't fix it by devising a more clever termination checker: deciding whether this particular function is total would be equivalent to settling the Collatz conjecture!

Another idea could be to express the concept "eventually reaches 1 in the Collatz sequence" as a recursively defined property of numbers CollatzHoldsFor : Nat → Prop. This is also rejected by the termination checker. In principle, we could convince Lean that div2 n is smaller than n by supplying an appropriate proof. However, we still can't convince it that (3 * n) + 1 is smaller than n!

def fail to show termination for CollatzHoldsFor with errors failed to infer structural recursion: Cannot use parameter n: failed to eliminate recursive application CollatzHoldsFor (div2 n) failed to prove termination, possible solutions: - Use `have`-expressions to prove the remaining goals - Use `termination_by` to specify a different well-founded relation - Use `decreasing_by` to specify your own tactic for discharging this kind of goal n x✝:Nat⊢ div2 n < x✝CollatzHoldsFor (n : Nat) : Prop := match n with | 0 => False | 1 => True | _ => bif n.even then CollatzHoldsFor (div2 n) else CollatzHoldsFor ((3 * n) + 1)
fail to show termination for
  CollatzHoldsFor
with errors
failed to infer structural recursion:
Cannot use parameter n:
  failed to eliminate recursive application
    CollatzHoldsFor (div2 n)


failed to prove termination, possible solutions:
  - Use `have`-expressions to prove the remaining goals
  - Use `termination_by` to specify a different well-founded relation
  - Use `decreasing_by` to specify your own tactic for discharging this kind of goal
n x✝:Nat⊢ div2 n < x✝

Fortunately, there is another way to do it: we can express the concept "reaches 1 eventually in the Collatz sequence" as an inductively defined property of numbers. Intuitively, this property is defined by a set of rules:

              ─────────────────── (one)
               CollatzHoldsFor 1

n.even = true     CollatzHoldsFor (div2 n)
─────────────────────────────────────────── (even)
               CollatzHoldsFor n

n.even = false    CollatzHoldsFor ((3 * n) + 1)
─────────────────────────────────────────────── (odd)
               CollatzHoldsFor n

So there are three ways to prove that a number n eventually reaches 1 in the Collatz sequence:

  • n is 1;

  • n is even and div2 n eventually reaches 1;

  • n is odd and (3 * n) + 1 eventually reaches 1.

We can prove that a number reaches 1 by constructing a (finite) derivation using these rules. For instance, here is the derivation proving that 12 reaches 1 (where we leave out the evenness/oddness premises):

─────────────────────── (one)
  CollatzHoldsFor 1
─────────────────────── (even)
  CollatzHoldsFor 2
─────────────────────── (even)
  CollatzHoldsFor 4
─────────────────────── (even)
  CollatzHoldsFor 8
─────────────────────── (even)
  CollatzHoldsFor 16
─────────────────────── (odd)
  CollatzHoldsFor 5
─────────────────────── (even)
  CollatzHoldsFor 10
─────────────────────── (odd)
  CollatzHoldsFor 3
─────────────────────── (even)
  CollatzHoldsFor 6
─────────────────────── (even)
  CollatzHoldsFor 12

Formally in Lean, the CollatzHoldsFor property is inductively defined:

inductive CollatzHoldsFor : Nat → Prop where | one : CollatzHoldsFor 1 | even {n : Nat} (h₁ : n.even = true) (h₂ : CollatzHoldsFor (div2 n)) : CollatzHoldsFor n | odd {n : Nat} (h₁ : n.even = false) (h₂ : CollatzHoldsFor ((3 * n) + 1)) : CollatzHoldsFor n

For particular numbers, we can now prove that the Collatz sequence reaches 1 (we'll look more closely at how it works a bit later in the chapter). Each step applies a rule and discharges the boolean evenness premise by rfl; the recursive premise is then reduced by the kernel from CollatzHoldsFor (div2 12) to CollatzHoldsFor 6, etc.

example : CollatzHoldsFor 12 := ⊢ CollatzHoldsFor 12 ⊢ Nat.even 12 = true⊢ CollatzHoldsFor (div2 12); ⊢ CollatzHoldsFor (div2 12) ⊢ (div2 12).even = true⊢ CollatzHoldsFor (div2 (div2 12)); ⊢ CollatzHoldsFor (div2 (div2 12)) ⊢ (div2 (div2 12)).even = false⊢ CollatzHoldsFor (3 * div2 (div2 12) + 1); ⊢ CollatzHoldsFor (3 * div2 (div2 12) + 1) ⊢ (3 * div2 (div2 12) + 1).even = true⊢ CollatzHoldsFor (div2 (3 * div2 (div2 12) + 1)); ⊢ CollatzHoldsFor (div2 (3 * div2 (div2 12) + 1)) ⊢ (div2 (3 * div2 (div2 12) + 1)).even = false⊢ CollatzHoldsFor (3 * div2 (3 * div2 (div2 12) + 1) + 1); ⊢ CollatzHoldsFor (3 * div2 (3 * div2 (div2 12) + 1) + 1) ⊢ (3 * div2 (3 * div2 (div2 12) + 1) + 1).even = true⊢ CollatzHoldsFor (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)); ⊢ CollatzHoldsFor (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)) ⊢ (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)).even = true⊢ CollatzHoldsFor (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))); ⊢ CollatzHoldsFor (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))) ⊢ (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))).even = true⊢ CollatzHoldsFor (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)))); ⊢ CollatzHoldsFor (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)))) ⊢ (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)))).even = true⊢ CollatzHoldsFor (div2 (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))))); ⊢ CollatzHoldsFor (div2 (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))))) All goals completed! 🐙

The Collatz conjecture then states that the sequence beginning from any positive number reaches 1:

def Collatz := ∀ n : Nat, n ≠ 0 → CollatzHoldsFor n

If you succeed in proving this conjecture, you've got a bright future as a number theorist! But don't spend too long on it — it's been open since 1937.

Note to developers (Chris Henson @chenson2018)

We may want to add an exercise later proving false if one assumes Collatz' conjecture without the n ≠ 0 assumption. We had that mistake in the script for years and no one noticed, wow!

theorem Collatz0' {n : Nat} (h₀ : n = 0) : ¬ CollatzHoldsFor n := n:Nath₀:n = 0⊢ ¬CollatzHoldsFor n n:Nath₀:n = 0h:CollatzHoldsFor n⊢ False induction h with n:Nath₀:1 = 0⊢ False All goals completed! 🐙 n:Natn✝:Nath₁✝:n✝.even = trueh₂✝:CollatzHoldsFor (div2 n✝)ih:div2 n✝ = 0 → Falseh₀:n✝ = 0⊢ False n:Natn✝:Nath₁✝:n✝.even = trueh₂✝:CollatzHoldsFor (div2 n✝)ih:div2 n✝ = 0 → Falseh₀:n✝ = 0⊢ div2 n✝ = 0; n:Natn✝:Nath₁✝:n✝.even = trueh₂✝:CollatzHoldsFor (div2 n✝)ih:div2 n✝ = 0 → Falseh₀:n✝ = 0⊢ div2 0 = 0; All goals completed! 🐙 n:Natn✝:Nath:n✝.even = falseh₂✝:CollatzHoldsFor (3 * n✝ + 1)h₂_ih✝:3 * n✝ + 1 = 0 → Falseh₀:n✝ = 0⊢ False n:Natn✝:Nath:Nat.even 0 = falseh₂✝:CollatzHoldsFor (3 * n✝ + 1)h₂_ih✝:3 * n✝ + 1 = 0 → Falseh₀:n✝ = 0⊢ False; n:Natn✝:Nath:true = falseh₂✝:CollatzHoldsFor (3 * n✝ + 1)h₂_ih✝:3 * n✝ + 1 = 0 → Falseh₀:n✝ = 0⊢ False; All goals completed! 🐙 theorem Collatz0 : ¬ (∀ n, CollatzHoldsFor n) := ⊢ ¬∀ (n : Nat), CollatzHoldsFor n h:∀ (n : Nat), CollatzHoldsFor n⊢ False; h:∀ (n : Nat), CollatzHoldsFor n⊢ ?n = 0h:∀ (n : Nat), CollatzHoldsFor n⊢ CollatzHoldsFor ?nh:∀ (n : Nat), CollatzHoldsFor n⊢ Nat; h:∀ (n : Nat), CollatzHoldsFor n⊢ CollatzHoldsFor 0; All goals completed! 🐙

9.1.2. Example: Binary Relation for Comparing Numbers🔗

A binary relation on a set α has Lean type α → α → Prop. This is a family of propositions parameterized by two elements of α — i.e., a proposition about pairs of elements of α.

For example, one familiar binary relation on Nat is Le : Nat → Nat → Prop, the less-than-or-equal-to relation, which can be inductively defined by the following two rules:

  ─────── (le_refl)
  Le n n

  Le n m
──────────── (le_step)
Le n (m + 1)
namespace LePlayground inductive Le : Nat → Nat → Prop where | refl {n : Nat} : Le n n | step {n m : Nat} (h : Le n m) : Le n (m + 1) scoped infix:50 (priority := high) " ≤ " => Le example : 3 ≤ 5 := ⊢ 3 ≤ 5 ⊢ 3 ≤ 4; ⊢ 3 ≤ 3; All goals completed! 🐙 end LePlayground

9.1.3. Example: Transitive Closure🔗

Another example: the transitive closure of a relation r is the smallest relation that contains r and that is transitive. This can be defined by the following two rules:

              r x y
         ─────────────── (t_step)
         TransGen r x y

TransGen r x y    TransGen r y z
──────────────────────────────────── (t_trans)
         TransGen r x z

In Lean this looks as follows:

inductive TransGen {α : Type} (r : α → α → Prop) : α → α → Prop where | step {x y : α} (h : r x y) : TransGen r x y | trans {x y z : α} (h₁ : TransGen r x y) (h₂ : TransGen r y z) : TransGen r x z

"Gen" is short for "generated by" — TransGen r means the smallest transitive relation generated by r.

For example, suppose we define a "parent of" relation on a group of people...

inductive Person : Type where | sage | cleo | ridley | moss inductive ParentOf : Person → Person → Prop where | sage_cleo : ParentOf .sage .cleo | sage_ridley : ParentOf .sage .ridley | cleo_moss : ParentOf .cleo .moss

The ParentOf relation is not transitive, but we can define an "ancestor of" relation as its transitive closure:

def AncestorOf : Person → Person → Prop := TransGen ParentOf

Here is a derivation showing that sage is an ancestor of moss:

 ——————————————————— (sage_cleo) ——————————————————— (cleo_moss)
 ParentOf .sage .cleo            ParentOf .cleo .moss
————————————————————— (step)    ————————————————————— (step)
AncestorOf .sage .cleo          AncestorOf .cleo .moss
———————————————————————————————————————————————————— (trans)
                AncestorOf .sage .moss
example : AncestorOf .sage .moss := ⊢ AncestorOf sage moss ⊢ TransGen ParentOf sage ?y⊢ TransGen ParentOf ?y moss⊢ Person ⊢ TransGen ParentOf sage ?y ⊢ ParentOf sage ?y✝; All goals completed! 🐙 ⊢ TransGen ParentOf cleo moss ⊢ ParentOf cleo moss; All goals completed! 🐙
Note to developers (Chris Henson)

A simple exercise could be nice here?

9.1.4. Example: Reflexive and Transitive Closure🔗

As another example, the reflexive and transitive closure of a relation r is the smallest relation that contains r and that is reflexive and transitive. This can be defined by the following three rules (where we added a reflexivity rule to TransGen):

                   r x y
         ——————————————————————— (step)
           ReflTransGen r x y

         ——————————————————————— (refl)
           ReflTransGen r x x

   ReflTransGen r x y    ReflTransGen r y z
—————————————————————————————————————————————— (trans)
           ReflTransGen r x z
inductive ReflTransGen {α : Type} (r : α → α → Prop) : α → α → Prop where | step {x y : α} (h : r x y) : ReflTransGen r x y | refl {x : α} : ReflTransGen r x x | trans {x y z : α} (h₁ : ReflTransGen r x y) (h₂ : ReflTransGen r y z) : ReflTransGen r x z

For instance, this enables an equivalent definition of the Collatz conjecture. First we define a binary relation corresponding to the "Collatz step function" collatzStep:

def CollatzStep (n m : Nat) : Prop := collatzStep n = m

This Collatz step relation can be used in conjunction with the reflexive and transitive closure operation to define a Collatz multi-step relation, expressing that a number n reaches another number m in zero or more Collatz steps:

def CollatzStepMulti (n m : Nat) : Prop := ReflTransGen CollatzStep n m def Collatz' : Prop := ∀ (n : Nat), n ≠ 0 → CollatzStepMulti n 1

9.1.5. Example: Permutations🔗

The familiar mathematical concept of permutation also has an elegant formulation as an inductive relation. For simplicity, let's focus on permutations of lists with exactly three elements.

We can define such permutations by the following rules:

   ───────────────────────── (swap12)
   Perm3 [a, b, c] [b, a, c]

   ───────────────────────── (swap23)
   Perm3 [a, b, c] [a, c, b]

Perm3 l₁ l₂       Perm3 l₂ l₃
───────────────────────────── (trans)
         Perm3 l₁ l₃

For instance, we can derive Perm3 [1, 2, 3] [3, 2, 1] as follows:

───────────────────────── (swap12)  ─────────────────────── (swap23)
Perm3 [1, 2, 3] [2, 1, 3]            Perm3 [2, 1, 3] [2, 3, 1]
─────────────────────────────────────────────────────────────────(trans)    ───────────────────── (swap12)
Perm3 [1, 2, 3] [2, 3, 1]                                                    Perm3 [2, 3, 1] [3, 2, 1]
───────────────────────────────────────────────────────────────────────────────────────────────────────── (trans)
Perm3 [1, 2, 3] [3, 2, 1]

In Lean, we can define Perm3 as follows:

inductive Perm3 {α : Type} : List α → List α → Prop where | swap12 {x y z : α} : Perm3 [x, y, z] [y, x, z] | swap23 {x y z : α} : Perm3 [x, y, z] [x, z, y] | trans {l₁ l₂ l₃ : List α} (h₁₂ : Perm3 l₁ l₂) (h₂₃ : Perm3 l₂ l₃) : Perm3 l₁ l₃

9.1.6. Example: Evenness (yet again)🔗

We've already seen two ways of stating a proposition that a number n is even: We can say

(1) Nat.even n = true (using the recursive boolean function Nat.even), or

(2) ∃ k, n = Nat.double k (using an existential quantifier).

A third possibility, which we'll use as a simple running example in this chapter, is to say that a number is even if we can establish its evenness from the following two rules:

  ────────── (zero)
    Even 0

    Even n
—————————————— (succ_succ)
  Even (n + 2)

To illustrate how this new definition of evenness works, let's imagine using it to show that 4 is even:

                 ──────── (zero)
                  Even 0
          ─────────────────────── (succ_succ)
          Even (.succ (.succ 0))
────────────────────────────────────────────── (succ_succ)
Even (.succ (.succ (.succ (.succ 0))))

We can translate the informal definition of evenness from above into a formal inductive declaration, where each "way that a number can be even" corresponds to a separate constructor:

inductive Even : Nat → Prop where | zero : Even 0 | succ_succ {n : Nat} (h : Even n) : Even (n + 2)

There are both similarities and a few differences between inductive properties like Even and the inductive types like Nat or List that we have been using throughout the course:

inductive `List` has already been declaredList (α : Type) : Type where | nil : List α | cons (x : α) (l : List α) : List α

The most important difference is that the constructors of Even, Even.zero and Even.succ_succ, yield different types (Even 0 and Even (n + 2)), whereas the List constructors both build List α values.

We can think of the inductive definition of Even as defining a Lean property Even : Nat → Prop, together with two "evidence constructors":

Even : Nat → Prop#check (Even) Even.zero : Even 0#check Even.zero Even.succ_succ {n : Nat} (h : Even n) : Even (n + 2)#check Even.succ_succ
Even : Nat → Prop
Even.zero : Even 0
Even.succ_succ {n : Nat} (h : Even n) : Even (n + 2)

These evidence constructors can be thought of as "primitive evidence of evenness," and they can be used later on just like proven theorems. In particular, we can use Lean's apply and exact tactics with the constructor names to obtain evidence for Even of particular numbers...

namespace Even example : Even 4 := ⊢ Even 4 ⊢ Even 2; ⊢ Even 0; All goals completed! 🐙

... or we can use function application syntax to combine several constructors:

example : Even 4 := ⊢ Even 4 All goals completed! 🐙

... or we can also use the constructor tactic we saw earlier to select the appropriate inductive constructor:

example : Even 4 := ⊢ Even 4 ⊢ Even 2; ⊢ Even 0; All goals completed! 🐙

In this way, we can also prove theorems that have hypotheses involving Even.

theorem plus4 (n : Nat) (h : Even n) : Even (4 + n) := n:Nath:Even n⊢ Even (4 + n) n:Nath:Even n⊢ Even (n + 4) All goals completed! 🐙 end Even

9.1.7. Constructing Evidence for Permutations🔗

Similarly, we can apply the evidence constructors to obtain evidence of Perm3 [1, 2, 3] [3, 2, 1]:

namespace Perm3 theorem rev : Perm3 [1, 2, 3] [3, 2, 1] := ⊢ Perm3 [1, 2, 3] [3, 2, 1] ⊢ Perm3 [1, 2, 3] [2, 3, 1]⊢ Perm3 [2, 3, 1] [3, 2, 1] ⊢ Perm3 [1, 2, 3] [2, 3, 1] ⊢ Perm3 [1, 2, 3] [2, 1, 3]⊢ Perm3 [2, 1, 3] [2, 3, 1] ⊢ Perm3 [1, 2, 3] [2, 1, 3] All goals completed! 🐙 ⊢ Perm3 [2, 1, 3] [2, 3, 1] All goals completed! 🐙 ⊢ Perm3 [2, 3, 1] [3, 2, 1] All goals completed! 🐙

And again we can equivalently use function application syntax to combine several constructors. (Note that the Lean type checker can infer not only types, but also Nats and Lists, when they are clear from the context.)

theorem rev' : Perm3 [1, 2, 3] [3, 2, 1] := ⊢ Perm3 [1, 2, 3] [3, 2, 1] All goals completed! 🐙

So the informal derivation trees we drew above are not too far from what's happening formally. Formally, we're using the evidence constructors to build evidence trees, similar to the finite trees we built using the constructors of data types such as Nat, List, binary trees, etc.

end Perm3

9.2. Using Evidence in Proofs🔗

Besides constructing evidence that numbers are even, we can also destruct such evidence, reasoning about how it could have been built — i.e., we can introduce and eliminate Even evidence, in the sense of Logic.

Defining Even with an inductive declaration tells Lean not only that the constructors Even.zero and Even.succ_succ are valid ways to build evidence that some number is Even, but also that these two constructors are the only ways to build evidence that numbers are Even.

In other words, if someone gives us evidence e for the proposition Even n, then we know that e must be one of two things:

  • e = Even.zero and n = 0, or

  • e = Even.succ_succ n' e' and n = n' + 2, where e' is evidence for Even n'.

This suggests that it should be possible to do case analysis and even induction on evidence of evenness...

9.2.1. Destructing and Inverting Evidence🔗

We can prove our characterization of evidence for Even n, using cases.

theorem Even.inversion (n : Nat) (h : Even n) : (n = 0) ∨ ∃ n', n = n' + 2 ∧ Even n' := n:Nath:Even n⊢ n = 0 ∨ ∃ n', n = n' + 2 ∧ Even n' cases h with ⊢ 0 = 0 ∨ ∃ n', 0 = n' + 2 ∧ Even n' ⊢ 0 = 0; All goals completed! 🐙 n:Nath:Even n⊢ n + 2 = 0 ∨ ∃ n', n + 2 = n' + 2 ∧ Even n' n:Nath:Even n⊢ ∃ n', n + 2 = n' + 2 ∧ Even n'; All goals completed! 🐙

Facts like this are often called "inversion lemmas" because they allow us to "invert" some given information to reason about all the different ways it could have been derived.

Quiz

Which tactics are needed to prove this goal?

∀ (n : Nat), Even n → n = 1 → true = false

(A) cases (B) contradiction (C) Both cases and contradiction (D) These tactics are not sufficient to solve the goal.

Show solution
example (n : Nat) (hEven : Even n) (h : n = 1) : true = false := n:NathEven:Even nh:n = 1⊢ true = false cases hEven with h:0 = 1⊢ true = false All goals completed! 🐙 n✝:Nath✝:Even n✝h:n✝ + 2 = 1⊢ true = false n✝:Nath✝:Even n✝n_eq✝:n✝ + 1 = 0⊢ true = false; All goals completed! 🐙

We can use the inversion lemma that we proved above to help structure proofs:

theorem Even.of_succ_succ (n : Nat) (h : Even (n + 2)) : Even n := n:Nath:Even (n + 2)⊢ Even n n:Nath:n + 2 = 0 ∨ ∃ n', n + 2 = n' + 2 ∧ Even n'⊢ Even n n:Natn':Nath₁:n + 2 = n' + 2h₂:Even n'⊢ Even n n:Natn':Nath₂:Even n'heq:n = n'⊢ Even n n:Nath₂:Even n⊢ Even n All goals completed! 🐙

Note: the subst h rewrites both the hypotheses and the goal using equation h : x = t from the context, and then drops h.

We've provided a handy tactic called inversion that does the work of our inversion lemma and more besides.

example (n : Nat) (h : Even (n + 2)) : Even n := n:Nath:Even (n + 2)⊢ Even n n:Nath✝:Even n⊢ Even n; All goals completed! 🐙

Recall from the Logic chapter that equality (Eq) is itself an inductively defined proposition, so inversion can also be used on equality propositions.

We can use inversion to re-prove some theorems from Tactics.

example (n m o : Nat) (h : [n, m] = [o, o]) : [n] = [m] := n:Natm:Nato:Nath:[n, m] = [o, o]⊢ [n] = [m] n:Nat⊢ [n] = [n]; All goals completed! 🐙 example (n : Nat) (h : n + 1 = 0) : 2 + 2 = 5 := n:Nath:n + 1 = 0⊢ 2 + 2 = 5 All goals completed! 🐙

For the inductively defined propositions we use, inversion behaves much like cases: it performs case analysis on the constructors of the hypothesis's inductive type. However, when the case analysis on an indexed proposition gives unsolvable equations between its indices, cases itself fails, whereas inversion leaves such equations in the context.

For example, cases would immediately fail on h:

example (n : Nat) (h : Even (n * n)) : n * n = 0 ∨ ∃ m, n * n = m + 2 := n:Nath:Even (n * n)⊢ n * n = 0 ∨ ∃ m, n * n = m + 2 All goals completed! 🐙
Dependent elimination failed: Failed to solve equation
  n.mul n = 0

inversion instead leaves the equations in the context, where we can use them directly:

example (n : Nat) (h : Even (n * n)) : n * n = 0 ∨ ∃ m, n * n = m + 2 := n:Nath:Even (n * n)⊢ n * n = 0 ∨ ∃ m, n * n = m + 2 inversion h with | zero => n:Nath:Even (n * n)h✝¹:h ≍ Even.zeroh✝:n.mul n = 0⊢ n * n = 0; All goals completed! 🐙 | succ_succ m' _ _ _ => n:Nath:Even (n * n)m':Nath✝²:Even m'h✝¹:h ≍ ⋯h✝:n.mul n = (m'.add 1).succ⊢ ∃ m, n * n = m + 2; All goals completed! 🐙
Quiz

Which tactics are needed to prove this goal, in addition to apply or exact?

∀ n, Even (2 + n) → Even n

(A) inversion (B) inversion, injections (C) inversion, rw [Nat.add_comm] (D) inversion, rw [Nat.add_comm], injections

Show solution
example (n : Nat) (h : Even (2 + n)) : Even n := n:Nath:Even (2 + n)⊢ Even n n:Nath:Even (n + 2)⊢ Even n inversion h with | _ h => All goals completed! 🐙

9.2.2. Induction on Evidence🔗

Let's try to show that our new notion of evenness implies our earlier notion (the one based on Nat.double).

example (n : Nat) (h : Even n) : Nat.Even n := n:Nath:Even n⊢ n.Even unsolved goals n':Nath':Even n'⊢ (n' + 2).Eveninversion h with | zero => All goals completed! 🐙 -- The first case can be solved trivially. | succ_succ n' h' => unexpected end of input; expected '{'

Unfortunately, the second case is harder. We need to show ∃ n₀, n' + 2 = double n₀, but the only available assumption is h', which states that Even n' holds. In other words, what we need here is precisely the result we are trying to prove, but applied to the smaller evidence h'.

If this story feels familiar, it is no coincidence: we encountered similar problems in the Induction chapter, when trying to use case analysis to prove results that required induction. And once again the solution is... induction!

Let's try proving that lemma again:

theorem Even.nat_even (n : Nat) (h : Even n) : Nat.Even n := n:Nath:Even n⊢ n.Even induction h with n:Nat⊢ Nat.Even 0 All goals completed! 🐙 -- (`0 = double 0` is closed by `exists`'s final `rfl`) n:Natn✝:Nath':Even n✝ih:n✝.Even⊢ (n✝ + 2).Even n:Natn✝:Nath':Even n✝ih:n✝.Evenk:Nathk:n✝ = k.double⊢ (n✝ + 2).Even n:Natn✝:Nath':Even n✝ih:n✝.Evenk:Nathk:n✝ = k.double⊢ n✝ + 2 = (k + 1).double; All goals completed! 🐙

Recall the definition of List.In from last chapter:

def List.In {α : Type} (x : α) (xs : List α) : Prop := match xs with | [] => False | x' :: xs' => x = x' ∨ In x xs'

We can also write this definition inductively like so:

inductive List.In' {α : Type} (x : α) : List α → Prop | head {l : List α} : In' x (x :: l) | tail {y : α} {l : List α} (h : In' x l) : In' x (y :: l)

In fact, this is exactly how Lean defines this proposition, which it calls Membership.mem and which is written x ∈ l. Its negation ¬ x ∈ l is also written as x ∉ l.

The characterizing lemmas for ∈ are called List.mem_nil_iff and List.mem_cons.

9.2.3. Multiple Induction Hypotheses🔗

9.3. Exercises with Inductive Relations🔗

9.3.1. More Facts about Le🔗

9.3.1.1. Facts about ≤🔗

9.3.1.2. Facts about < and ≥🔗

9.3.1.3. Relating Le and Nat.ble🔗

9.4. Additional Exercises🔗

Source revision: e85fe77, committed 2026-10-06 21:16 UTC