Lean proofs are terms. Tactic mode is a way to construct those terms incrementally. Term mode writes the final object directly. Both styles produce the same kernel-checked result. The choice affects readability, locality of reasoning, and how easily a proof evolves.
Problem
You want to decide when to write a proof as a term, when to use tactics, and how to translate between the two styles.
Solution
Recognize that every tactic proof has a corresponding term proof. Use term mode for small, compositional proofs and tactic mode for stepwise transformations.
Example: Identity
Term mode:
theorem id_term (P : Prop) : P -> P :=
fun h => hTactic mode:
theorem id_tactic (P : Prop) : P -> P := by
intro h
exact hBoth have the same type:
#check id_term
#check id_tacticReading Tactic Proofs as Terms
A tactic script corresponds to nested function applications and lambda abstractions.
theorem mp_tactic (P Q : Prop) (hp : P) (hpq : P -> Q) : Q := by
exact hpq hpTerm version:
theorem mp_term (P Q : Prop) (hp : P) (hpq : P -> Q) : Q :=
hpq hpThe tactic exact t inserts the term t.
Building Structure: Conjunction
Term mode:
theorem and_intro_term (P Q : Prop) (hp : P) (hq : Q) : P ∧ Q :=
And.intro hp hqTactic mode:
theorem and_intro_tactic (P Q : Prop) (hp : P) (hq : Q) : P ∧ Q := by
constructor
· exact hp
· exact hqThe constructor And.intro is applied explicitly in term mode and implicitly by constructor in tactic mode.
Case Analysis: Disjunction
Term mode:
theorem or_comm_term (P Q : Prop) : P ∨ Q -> Q ∨ P :=
fun h =>
Or.elim h
(fun hp => Or.inr hp)
(fun hq => Or.inl hq)Tactic mode:
theorem or_comm_tactic (P Q : Prop) : P ∨ Q -> Q ∨ P := by
intro h
cases h with
| inl hp =>
right
exact hp
| inr hq =>
left
exact hqThe eliminator Or.elim corresponds to cases.
Intermediate Results
Term mode often uses let or nested functions.
theorem chain_term (P Q R : Prop)
(hpq : P -> Q) (hqr : Q -> R) : P -> R :=
fun hp => hqr (hpq hp)Tactic mode uses have:
theorem chain_tactic (P Q R : Prop)
(hpq : P -> Q) (hqr : Q -> R) : P -> R := by
intro hp
have hq : Q := hpq hp
have hr : R := hqr hq
exact hrBoth express the same data flow. Tactic mode names intermediate steps; term mode composes functions.
Rewriting
Term mode:
theorem rw_term (a b : Nat) (h : a = b) : a + 1 = b + 1 :=
Eq.subst h rflTactic mode:
theorem rw_tactic (a b : Nat) (h : a = b) : a + 1 = b + 1 := by
rw [h]The tactic hides the underlying substitution term.
Function Extensionality
Term mode:
theorem fun_eq_term :
(fun n : Nat => n + 0) = (fun n => n) :=
funext (fun n => by simp)Tactic mode:
theorem fun_eq_tactic :
(fun n : Nat => n + 0) = (fun n => n) := by
funext n
simpfunext introduces an arbitrary input and reduces function equality to pointwise equality.
When to Use Term Mode
Use term mode when:
- the proof is a single expression
- the structure is clear from function composition
- you want compact, compositional code
- the proof resembles a direct computation
Example:
theorem const_term {α β : Type} (x : α) (y : β) : α :=
xWhen to Use Tactic Mode
Use tactic mode when:
- the proof has many steps
- you need case analysis or induction
- intermediate results help readability
- you want to inspect goals interactively
Example:
theorem and_comm_tactic (P Q : Prop) : P ∧ Q -> Q ∧ P := by
intro h
constructor
· exact h.right
· exact h.leftMixing Styles
You can mix styles within a single proof.
theorem mixed_example (P Q : Prop) (hp : P) (hq : Q) : P ∧ Q := by
exact And.intro hp hqOr use term fragments inside tactics:
theorem mixed_example2 (P Q : Prop) (hp : P) (hq : Q) : P ∧ Q := by
constructor
· exact hp
· exact hqBoth are valid. Choose the style that keeps the proof readable.
Inspecting the Generated Term
Lean can show the term produced by a tactic proof.
#print id_tacticThis reveals the underlying lambda term. Reading such output helps connect tactic steps with term structure.
Common Pitfalls
If a tactic proof becomes long, consider rewriting it as a term or splitting it into lemmas.
If a term proof becomes unreadable, switch to tactic mode and name intermediate steps.
If a tactic hides too much detail, replace it with explicit terms or smaller tactics.
If a term uses complex nested lambdas, introduce local definitions or use tactic mode.
Recipe: Translate Between Styles
Start with a tactic proof:
theorem ex (P Q : Prop) (hp : P) (hq : Q) : Q ∧ P := by
constructor
· exact hq
· exact hpTranslate to term mode:
theorem ex_term (P Q : Prop) (hp : P) (hq : Q) : Q ∧ P :=
And.intro hq hpThen compare with #check or #print.
This exercise builds intuition for both styles.
Takeaway
Term mode and tactic mode are two interfaces to the same underlying system. Term mode emphasizes direct construction and compositional clarity. Tactic mode emphasizes stepwise reasoning and local control. Effective Lean code uses both, choosing the style that best matches the structure of the argument.