simp

Summary

There are many lemmas of the form x = y or P Q in mathlib which are “tagged” with the @[simp] tag. Note that these kinds of lemmas are ones for which the rw tactic can be used. A lemma tagged with @[simp] is called a “simp lemma”.

When Lean’s simplifier simp is run, it tries to find simp lemmas for which the left hand side of the lemma is in the goal. It then rewrites the lemma and continues. Ultimately what happens is that the goal ends up simplified, and, ideally, solved.

Overview

There are hundreds of lemmas in mathlib of the form x + 0 = x or x * 0 = 0, where one side is manifestly simpler than the other (in the sense that a mathematician would instinctively replace the more complex side by the simpler side if they were trying to prove a theorem). In Lean, such a lemma might be tagged with the @[simp] tag. A tag on a lemma or definition does nothing mathematical; it is just a flag for certain tactics. The convention for @[simp] lemmas in Lean is that the left hand side of such a lemma should be the more complicated side. For example Lean has

@[simp] add_zero (x) : x + 0 = x := ...
@[simp] zero_add (x) : 0 + x = x := ...

(proofs omitted). Because these lemmas are tagged with @[simp], the following proof works:

example (x : ) : 0 + 0 + x + 0 + 0 = (x + (0 + 0)) :=
begin
  simp
end

If you want to know which lemmas simp used, you can use the related tactic squeeze_simp, which gives an output listing the theorems which simp rewrote to make the progress which it made.

Examples

  1. If you are doing a proof by induction, then simp will often deal with the base case, because it knows lots of ways to simplify goals with a 0 in. Here is simp being used to prove that \(\sum_{i=0}^{n-1}i=n(n-1)/2\):

import data.finset.basic
import data.real.basic

open_locale big_operators

open finset

example (n : ) :
   i in range n, (i : ) = n * (n - 1) / 2 :=
begin
  induction n with d hd,
  { -- base case: sum over empty type is 0 * (0 - 1) / 2
    simp },
  { -- inductive step
    rw [sum_range_succ, hd],
    simp, -- tidies up and reduces the goal to
    -- ⊢ ↑d * (↑d - 1) / 2 + ↑d = (↑d + 1) * ↑d / 2
    ring, -- a more appropriate tactic to finish the job
  }
end

Details

Note that simp, like rw, works up to syntactic equality. In other words, if your goal mentions x and there is a simp lemma h : x' = y where x' is definitionally, but not syntactically, equal to x, then simp will not do the rewrite; this is because rw h fails.

You can use simp on goals too: simp at h will run the simplifier on h.

There’s quite a lot to say about the simp tactic. More details of how to use it are in the explanation of how to use simp is in the community documentation and in section 5.7 of Theorem Proving In Lean .

Further notes

The two related tactics simp? and squeeze_simp both attempt to figure out exactly which lemmas simp used. Sometimes they give different answers; it turns out that because simp is written in C++ rather than in Lean, reverse-engineering it is a subtle problem.