Use when Lean 4 proofs have unnecessary have-blocks that could be inlined, or long/reused have-blocks that should be extracted to lemmas
Refactors Lean 4 have-blocks by inlining single-use proofs or extracting reusable ones to lemmas.
/plugin marketplace add cameronfreer/lean4-skills/plugin install lean4-theorem-proving@lean4-skillsInteractive workflow for refactoring have statements - either inlining them (proving directly at use site) or extracting them to separate helper lemmas.
First ask: Do you need the have at all?
Proofs with many have statements are often not idiomatic. In mathlib style, you frequently prove results inline where they're needed rather than naming intermediates. Consider eliminating have entirely when:
Only keep have when:
exact h, rw [h])When to extract (if have is needed):
Reference: See proof-refactoring.md for detailed patterns and decision tree.
If file not specified, ask:
Which file would you like to refactor?
Scan for refactoring candidates:
Read the file and identify have statements that could be refactored (long proofs, single-use intermediates, or hurting readability).
Report candidates:
Found N have-blocks to consider in [file]:
1. Line 42: `have h_bound : ∀ i, k i < n := by` (47 lines)
Used: 1 time | Recommendation: inline or extract
2. Line 156: `have h_meas : Measurable f := by` (8 lines)
Used: 1 time | Recommendation: inline
3. Line 245: `have h_eq : μ = ν := by` (52 lines)
Used: 3 times | Recommendation: extract
Which would you like to refactor? (1/2/3/all/skip)
For chosen candidate, determine:
a) Usage analysis:
b) Recommend approach:
Analyzing `have h_bound : ∀ i, k i < n := by`...
Usage: h_bound used 1 time at line 89: `exact h_bound i`
Proof length: 47 lines
Options:
1. INLINE - eliminate have, prove directly at use site
2. EXTRACT - move to separate lemma (if reusable or clearer)
3. KEEP - leave as-is (if naming aids understanding)
Recommendation: INLINE (single use, can prove at call site)
Proceed with inline? (yes/extract/keep/cancel)
For multi-use:
Usage: h_meas used 3 times (lines 160, 172, 185)
Proof length: 35 lines
Options:
1. EXTRACT - move to separate lemma (recommended for multi-use)
2. KEEP - leave as-is
Recommendation: EXTRACT (reused, worth naming)
Proceed with extraction? (yes/keep/cancel)
Transform:
-- BEFORE:
have h_bound : ∀ i, k i < n := by
intro i
-- [proof]
exact h_bound i
-- AFTER:
exact (by intro i; ... : ∀ i, k i < n) i
-- or more idiomatically:
exact proof_term_here
Verify and report:
✅ Inlined successfully!
Changes:
- Removed: 47-line have-block
- Modified: line 89, now proves inline
Proof is now shorter and more direct.
Determine parameters needed:
Required parameters:
- k : Fin m → ℕ (used in goal)
- hk_mono : StrictMono k (used in proof)
- n : ℕ (appears in goal)
Optional parameters (could be inlined):
- m : ℕ (inferred from k's type)
Check if extractable:
let bindings? (May cause definitional issues)Propose extraction:
-- BEFORE (inline):
theorem main_theorem ... := by
...
have h_bound : ∀ i, k i < n := by
intro i
-- [30+ lines of proof here]
...
omega
...
-- AFTER (extracted):
private lemma helper_bound {m : ℕ} (k : Fin m → ℕ) (hk : StrictMono k)
(i : Fin m) : k i < k (Fin.last m) + 1 := by
have : k i ≤ k (Fin.last m) := StrictMono.monotone hk (Fin.le_last i)
omega
theorem main_theorem ... := by
...
have h_bound : ∀ i, k i < n := fun i => helper_bound k hk_mono i
...
Present options:
Proposed extraction:
Helper name: strictMono_bound (or suggest alternative)
Parameters: k, hk (minimal)
Visibility: private (proof-specific) or public?
Preview the change? (yes/different-name/more-params/cancel)
If user approves, apply the chosen refactoring:
For inline: a) Remove the have-block b) Replace uses with inline proof c) Verify compilation
For extract: a) Add helper lemma (before the theorem) b) Replace have-block with call to helper c) Verify compilation
lake build [file]
Report result:
✅ Refactoring successful!
Changes:
- [Inline] Removed: 47-line have-block, proved inline at use site
- [Extract] Added: private lemma strictMono_bound (12 lines), replaced 47-line have-block
Continue with next candidate? (yes/no)
If inline fails:
If extraction fails:
❌ Extraction failed: type mismatch
Analysis:
- Helper returns: k i < k (Fin.last m) + 1
- Original had: k i < n
- Issue: n was a let-binding that's not available in helper
Options:
1. Add n as parameter with equality proof
2. Try inline instead
3. Keep original (don't refactor)
4. Manual edit
Choose: (1/2/3/4)
Common extraction issues:
hparam : param = exprDecision tree:
have used only once? → Consider inlinehave used multiple times? → Consider extractInline checklist:
Extraction checklist:
Naming conventions (for extracted lemmas):
bounded_by_integral, measurable_compositionhelper1, aux, tempWhen to keep as-is:
If lean-lsp MCP available:
# Get goal at have-block location
lean_goal(file, line=have_line)
# Check for errors after extraction
lean_diagnostic_messages(file)
# Verify types align
lean_hover_info(file, line=helper_call_line, column)
/analyze-sorries - Find incomplete proofs/fill-sorry - Fill individual sorries/golf-proofs - Simplify after extraction/check-axioms - Verify no axioms introduced