Skip to content
import GlimpseOfLean.Library.Basic

Implications

Using implications

Lean denotes implication by the symbol instead of because it sees a proof of P → Q as a function sending any proof of P to a proof of Q (increase font size if you can't see the difference between → and ⇒).

For instance, given a real number a, the lemma sq_pos_of_pos claims 0 < a → 0 < a^2 so the proof belows apply the "function" sq_pos_of_pos to the assumption ha.

Remember that whenever you see in a Lean file a symbol that you don't see on your keyboard, such as →, you can put your mouse cursor above it and learn from a tooltip how to type it. In the case of →, you can type it by typing "\to ", so backslash-t-o-space.

example (a : ℝ) (ha : 0 < a) : 0 < a^2 := by
  exact sq_pos_of_pos ha

The above proof is a direct proof: we already know 0 < a and we feed this fact into the implication. We can also use backward reasoning using the apply tactic.

example (a : ℝ) (ha : 0 < a) : 0 < (a^2)^2 := by
  apply sq_pos_of_pos -- Thanks to `sq_pos_of_pos`, it suffices to prove `0 < a^2`
  apply sq_pos_of_pos -- Thanks to `sq_pos_of_pos`, it suffices to prove `0 < a`
  exact ha -- this is exactly our assumption `ha`.

Try to do the next exercise using the lemma add_pos : 0 < x → 0 < y → 0 < x + y. Note that after you apply add_pos you will have two goals, that you will have to prove one-by-one.

example (a b : ℝ) (ha : 0 < a) (hb : 0 < b) : 0 < a^2 + b^2 := by
  -- sorry
  apply add_pos
  apply sq_pos_of_pos
  exact ha
  apply sq_pos_of_pos
  exact hb
  -- sorry

You can also give a proof with forward reasoning, using the have tactic. In order to announce an intermediate statement we use:

have my_name : my_statement := by

and then increase the indentation level. This triggers the apparition of a new goal: proving the statement. After the proof is done, the statement becomes available under the name my_name. If the proof is a single exact tactic then you can get rid of by and exact and directly put the argument of exact after the :=.

example (a : ℝ) (ha : 0 < a) : 0 < (a^2)^2 := by
  have h2 : 0 < a^2 := by     -- we declare `0 < a^2` as a subgoal
    apply sq_pos_of_pos  -- we start proving the subgoal
    exact ha             -- this line is indented, so part of the proof of the subgoal
  exact sq_pos_of_pos h2 -- we finished the subgoal, and now we prove the main goal using it.

Now prove the same lemma as before using forwards reasoning.

example (a b : ℝ) (ha : 0 < a) (hb : 0 < b) : 0 < a^2 + b^2 := by
  -- sorry
  have h2a : 0 < a^2 := by
    exact sq_pos_of_pos ha
  have h2b : 0 < b^2 := sq_pos_of_pos hb -- using the condensed spelling
  exact add_pos h2a h2b
  -- sorry

Proving implications

In order to prove an implication, we need to assume the premise and prove the conclusion. This is done using the intro tactic. Secretly the exercise above was proving the implication a > 0 → (a^2)^2 > 0 but the premise was already introduced for us.

example (a b : ℝ) : a > 0 → b > 0 → a + b > 0 := by
  intro ha hb -- You can choose any names here
  exact add_pos ha hb

Now prove the following simple statement in propositional logic. Note that p → q → r means p → (q → r).

example (p q r : Prop) : (p → q) → (p → q → r) → p → r := by
  -- sorry
  intro h1 h2 h3
  apply h2 h3
  exact h1 h3
  -- sorry

Note that, when using intro, you need to give a name to the assumption. Lean will let you use a name that was already used. In that case the new assumption will shadow the existing one which becomes inaccessible. So the safe thing to do by default is to use a new name.

Equivalences

Using equivalences to rewrite statements

In the previous file, we saw how to rewrite using equalities. The analogue operation with mathematical statements is rewriting using equivalences. This is also done using the rw tactic. Lean uses to denote equivalence instead of (increase font size if you can't see the difference).

In the following exercises we will use the lemma:

sub_nonneg : 0 ≤ y - x ↔ x ≤ y

example {a b c : ℝ} : c + a ≤ c + b ↔ a ≤ b := by
  rw [← sub_nonneg] -- This `rw` uses an equivalence
  have key : (c + b) - (c + a) = b - a := by
    ring
  rw [key] -- This `rw` uses an equality result, not an equivalence
  rw [sub_nonneg] -- and we switch back to reach the tautology a ≤ b ↔ a ≤ b

Let's prove a variation

example {a b : ℝ} (c : ℝ) : a + c ≤ b + c ↔ a ≤ b := by
  -- sorry
  rw [← sub_nonneg]
  ring
  rw [sub_nonneg]
  -- sorry

The above lemma is already in the mathematical library, under the name add_le_add_iff_right:

add_le_add_iff_right (c : ℝ) : a + c ≤ b + c ↔ a ≤ b

This can be read as: "add_le_add_iff_right is a function that will take as input a real number c and will output a proof of a + c ≤ b + c ↔ a ≤ b". Here is an example where this lemma is used.

example {a b : ℝ}  (ha : 0 ≤ a) : b ≤ a + b := by
  calc
    b = 0 + b := by ring
    _ ≤ a + b := by rw [add_le_add_iff_right b] ; exact ha

Using equivalences as pairs of implications

The second line in the above proof is a bit silly: we use statement rewriting to reduce the goal to our assumption ha, but it would be more natural to see the equivalence as a double implication. We can access the two implications of an equivalence h : P ↔ Q as h.1 : P → Q and h.2 : Q → P. This allows us to rewrite the above proof as:

example {a b : ℝ}  (ha : 0 ≤ a) : b ≤ a + b := by
  calc
    b = 0 + b := by ring
    _ ≤ a + b := by exact (add_le_add_iff_right b).2 ha

Let's do a variant using add_le_add_iff_left a : a + b ≤ a + c ↔ b ≤ c instead.

example (a b : ℝ) (hb : 0 ≤ b) : a ≤ a + b := by
  -- sorry
  calc
    a = a + 0 := by ring
    _ ≤ a + b := by exact (add_le_add_iff_left a).2 hb
  -- sorry

Important note: in the previous exercises, we used lemmas like add_le_add_iff_left as elementary examples to manipulate equivalences. But invoking those lemmas by hand when working on interesting mathematics would be awfully tedious. There are tactics whose job is to do these things automatically, but this is not the topic of this file.

Proving equivalences

In order to prove an equivalence one can use rw until the goal is the tautology P ↔ P, just as one can do with equalities.

One can also separately prove the two implications using the constructor tactic. Below is an example. If you put your cursor after constructor, you will see two goals, one for each direction. Lean will keep track of the goals for you, making sure you solve all of them. The "focussing dot" · keeps the proof for each goal separate.

example (a b : ℝ) : (a-b)*(a+b) = 0 ↔ a^2 = b^2 := by
  constructor
  · intro h
    calc
      a ^ 2 = b^2 + (a - b) * (a + b)  := by ring
          _ = b^2 + 0                  := by rw [h]
          _ = b^2                      := by ring
  · intro h
    calc
      (a-b)*(a+b) = a^2 - b^2  := by ring
                _ = b^2 - b^2  := by rw [h]
                _ = 0          := by ring

You can try it yourself in this exercise.

example (a b : ℝ) : a = b ↔ b - a = 0 := by
  -- sorry
  constructor
  · intro h
    rw [h]
    ring
  · intro h
    calc
      a = b - (b - a) := by ring
      _ = b - 0       := by rw [h]
      _ = b           := by ring
  -- sorry

This is the end of this file where you learned how to handle implications and equivalences. You learned about tactics: * intro * apply * have * constructor