Zulip Chat Archive
Stream: new members
Topic: general introductory advice around tactics
JJ (Jul 31 2025 at 20:07):
Hello! I am working my way through Analysis I as a way to learn the theorem proving side of Lean and had a couple of general questions. These are not specific to Analysis I -- they're just questions I had (being new to tactics) so I figured I'd put them here.
- If I have a hypotheses ¬A and a goal A, how do I tell Lean this is false? (Encountering this using
by_cases.) -
Is there a nicer way to make a new hypothesis than (for example) constructingx++ = y++viahave hxy : x++ = a := hx; symm at hy; rw [hy] at hxy(forhx : x++ = a,hy : y++ a)? Ideally, I'd like to be able to do something likehave hxy : x++ = y++ := (rw [(symm hy)] at hx)(fictitious syntax) in a sorta functional manner, but I don't know if this is possible.
have hxy : x++ = y++ := hx.trans hy.symm -
On a similar tactic, can I get
abelor some other simple rewriting tactic to rewrite a goal for me overtly? i.e. I havehx : a + c = b + c + x, and I would like to say like.replace hx := c + a = c + (b + x) by abel. -
Is there a way to see the tacticssimpapplied to close a goal?
simp? -
Is
apply And.introorconstructormore idiomatic? -
How do I apply a theorem taking arguments to a goal? Specifically, I have⊢ c + a = c + (b + x)as my goal, and I have a theoremadd_left_cancel (a b c : Nat) (habc = a + b = a + c) : b = c. I would like to doapply add_left_cancel c a (b + x) <goal>, but this doesn't work.
forgot about backwards reasoning! normalapplyworks normally -
I have a hypothesis
h : d ≠ 0and a theoremisPos_iff (n : Nat) : n.IsPos ↔ n ≠ 0. How do I applyisPos_iffto the hypothesis? I think it is thatisPos_ifftakes an argument that is messing me up. This might be similar to the previous question, but since this is an ↔ I think that I should be able to do it withrewrite.
Thanks for any help!
Kenny Lau (Jul 31 2025 at 20:11):
2:
import Mathlib
example : 37 = 51 := by
transitivity 100
· show 37 = 100
sorry
· show 100 = 51
sorry
Aaron Liu (Jul 31 2025 at 20:12):
rw [← hy] and rw [hy.symm] are both things you can do
Kenny Lau (Jul 31 2025 at 20:13):
3:
import Mathlib
example {a b c x : ℤ} (hx : a + c = b + c + x) : 1 + (c + a) = c + (b + x) + 1 := by
rw [show c + a = c + (b + x) by abel_nf at hx ⊢; exact hx]
abel
Kenny Lau (Jul 31 2025 at 20:13):
4: simp?
Kenny Lau (Jul 31 2025 at 20:13):
5:
import Mathlib
example : 0 = 1 ∧ 1 = 0 := by
refine ⟨?_, ?_⟩
· sorry
· sorry
Aaron Liu (Jul 31 2025 at 20:14):
You can also have hxy : x++ = y++ := hx.trans hy.symm
Kenny Lau (Jul 31 2025 at 20:14):
1:
import Mathlib
example {A : Prop} (ha : A) (hna : ¬ A) : False := by
exact absurd ha hna
Aaron Liu (Jul 31 2025 at 20:15):
For number 6, what you are doing doesn't seem logically valid
Kenny Lau (Jul 31 2025 at 20:15):
6: what you're asking is not mathematically sound.
Kenny Lau (Jul 31 2025 at 20:15):
but there's rw [add_left_inj] instead
Kenny Lau (Jul 31 2025 at 20:16):
7 : rw [\l isPos_iff] at h
Matthew Jasper (Jul 31 2025 at 20:22):
For 6 there's also congr
JJ (Jul 31 2025 at 20:26):
6: Oh right yes, if I'm applying a theorem to a goal I'm reasoning backwards...
Michael Rothgang (Jul 31 2025 at 20:57):
Another option for 1:
import Mathlib
example {A : Prop} (ha : A) (hna : ¬ A) : False := by
apply hna ha
Michael Rothgang (Jul 31 2025 at 20:58):
This works because ¬ A is defined to be A → False
JJ (Jul 31 2025 at 21:08):
Okay, working through understanding these. I have run into some rw confusion, though. I have an ih : n + b = 0 → n = 0, an h : n++ + b = 0, and a goal ⊢ n++ + b = 0 (for (b n : Nat). I would like to somehow... apply ih to h and obtain n++ = 0.
But I can't apply ih. I think there's something I don't understand going on there: n : Nat and n++ : Nat so I don't see why I can't apply ih.
But there's also something else I don't understand going on: I can rw [ih] at h (giving h : 0++ + b = 0) and I can rw [← ih] at h (giving h : n++ + b = n). What is going on here? I thought rewrite only functioned on equalities or biconditionals? And why is it transforming my hypotheses to false statements?
Kyle Miller (Jul 31 2025 at 21:10):
Rewrite supports conditional rewrites. It interprets ih as n = 0, and it adds an extra n + b = 0 goal.
JJ (Jul 31 2025 at 21:11):
Hm! Okay, that's a little unexpected. I see the new case hind now though.
Aaron Liu (Jul 31 2025 at 21:21):
JJ said:
Okay, working through understanding these. I have run into some
rwconfusion, though. I have anih : n + b = 0 → n = 0, anh : n++ + b = 0, and a goal⊢ n++ + b = 0(for(b n : Nat). I would like to somehow... applyihtohand obtainn++ = 0.
have hnsucc := ih h
JJ (Jul 31 2025 at 21:23):
That doesn't work because ih expects n + b = 0, but h is n++ + b = 0.
Aaron Liu (Jul 31 2025 at 21:24):
ok so explain again why you think applying ih to h is something you can do here
JJ (Jul 31 2025 at 21:25):
Because ih expects something of the form (n : Nat) + (b : Nat) = 0 and h is of the form (n++ : Nat) + (b : Nat) = 0.
Aaron Liu (Jul 31 2025 at 21:26):
yes they don't match because n++ is not the same as n
JJ (Jul 31 2025 at 21:27):
Right. I'm trying to understand why this is.
Aaron Liu (Jul 31 2025 at 21:28):
You're trying to understand why n++ is not the same as n?
JJ (Jul 31 2025 at 21:29):
No, I'm trying to understand why ih cannot take in h.
Aaron Liu (Jul 31 2025 at 21:30):
ih cannot take in h because ih eats a proof of n + b = 0 and h is a proof of n++ + b = 0 and n + b = 0 is not the same as n++ + b = 0
JJ (Jul 31 2025 at 21:30):
Right, but n and b are arbitrary. So I don't get why n++ can't be considered n for the purpose of ih.
Aaron Liu (Jul 31 2025 at 21:31):
no they're not arbitrary
Aaron Liu (Jul 31 2025 at 21:31):
since you have intro'd them they are fixed now
JJ (Jul 31 2025 at 21:32):
Hm.
Aaron Liu (Jul 31 2025 at 21:32):
if it was arbitrary then ih would look like ∀ k l, k + l = 0 → k = 0
Aaron Liu (Jul 31 2025 at 21:32):
but no it looks like n + b = 0 → n = 0
Aaron Liu (Jul 31 2025 at 21:33):
you have fixed them here
JJ (Jul 31 2025 at 21:33):
Like my full tactic state is (b n : Nat); (ih : n + b = 0 → n = 0); (h : n++ + b = 0) and I'm trying to derive n++ = 0. I look at this, I think "hm -- ih tells me that given some arbitrary n and b, if n + b = 0 then n = 0." I look at this again, I think "hm -- h tells me that for some n++ I have n++ + b = 0". So then I think I can apply h to ih but I obviously can't and now I'm confused.
Aaron Liu (Jul 31 2025 at 21:34):
you are reverting your variables incorrectly
JJ (Jul 31 2025 at 21:34):
What does that mean?
Aaron Liu (Jul 31 2025 at 21:35):
I see this as
I have
bandnwhich areNat
I haveihwhich tells me that if I give it a proof ofn + b = 0then it will give me a proof ofn = 0
I havehwhich tells me thatn++ + b = 0
I need to prove thatn++ = 0
Aaron Liu (Jul 31 2025 at 21:36):
note the lack of arbitrariness here
JJ (Jul 31 2025 at 21:36):
Mmm, I see.
JJ (Jul 31 2025 at 21:38):
Ah! Right okay cool. I think I just closed it with induction. Which makes sense now, I think I was implicitly failing to consider the n = 0 case?
JJ (Jul 31 2025 at 21:41):
Or rather I guess this was by cases? Hrm. When n = 0, h : 0++ + b = 0 is false.
JJ (Jul 31 2025 at 21:44):
When n > 0... grr, I'm not sure what I actually just did. Is there a nice by_cases tactic that I should be using (over induction) for introductory stuff? by_cases itself is annoying, because it only takes in p and has the two cases be p and ¬p, and so far I don't actually know how to transform ¬a=0 into a ≠ 0. (And into a > 0, for that matter.)
Aaron Liu (Jul 31 2025 at 21:45):
¬a=0 is defeq to a ≠ 0
Aaron Liu (Jul 31 2025 at 21:46):
that means when you unfold all the definitions you get the same thing
Aaron Liu (Jul 31 2025 at 21:46):
so they're usually interchangeable
Aaron Liu (Jul 31 2025 at 21:47):
making this the same as a > 0 is a theorem
JJ (Jul 31 2025 at 21:50):
Ah, I found an example of what I was having trouble with. Yeah, it was not knowing how to convert ¬a = 0 to a ≠ 0.
JJ (Jul 31 2025 at 21:51):
So I've got a h : ¬d = 0 and an isPos_iff : (n : Nat) : n.isPos ↔ n ≠ 0. I couldn't rewrite [← isPos_iff] at h. But if I put replace h : d ≠ 0 := sorry before, rw [← isPos_iff] at h works fine.
JJ (Jul 31 2025 at 21:51):
It looks now like I can replace that sorry with h itself, but is there a way to avoid replace altogether?
Aaron Liu (Jul 31 2025 at 22:01):
rewrite with docs#ne_eq
Matt Diamond (Jul 31 2025 at 22:56):
Did people misread #1 or did JJ miswrite it? It says "If I have a hypotheses ¬A and a goal A, how do I tell Lean this is false?"
If you have a hypothesis ¬A and your goal is A, it's likely that something has gone wrong
JJ (Jul 31 2025 at 22:57):
Ah, sorry. The context is that's within a by_cases.
Matt Diamond (Jul 31 2025 at 22:58):
the context doesn't really matter, I'm asking if the goal is A or if the goal is False and you have both A and ¬A
JJ (Jul 31 2025 at 22:59):
The goal is A. I basically want to use absurd. Let me see if I can find where I was having trouble doing so.
JJ (Jul 31 2025 at 23:46):
Sorry! I can't find the place I was running into that. I think I rewrote it to not use by_cases.
JJ (Jul 31 2025 at 23:48):
Though speaking of by_cases: I have a lot of revert x; apply induction (this is equivalent to Mathlib induction I think, but is a theorem in the first chapter of this Analysis I text) scattered throughout, including in some places where I only really need by_cases. But in a lot of those induction places simp works where it doesn't in by_cases. Is by_cases the idiomatic cases tactic to use, or is there something better?
Aaron Liu (Jul 31 2025 at 23:49):
For doing cases it depends on what I'm casing on
Aaron Liu (Jul 31 2025 at 23:50):
sometimes I use by_cases but if there's a more convenient form I will use obtain with the lemma
Aaron Liu (Jul 31 2025 at 23:50):
like for example docs#eq_or_ne, docs#eq_or_lt_of_le, docs#Set.eq_empty_or_nonempty
Matt Diamond (Jul 31 2025 at 23:52):
oh yeah, or docs#Option.eq_none_or_eq_some ... doing rcases or obtain with those can be useful if you need more than just a simple P ∨ ¬P
Aaron Liu (Jul 31 2025 at 23:52):
or sometimes I will use match if I have a way of solving it for all n > 8 for example, and the n ≤ 8 is a case bash
JJ (Aug 01 2025 at 00:40):
Nice thanks! It seems like match is pretty amenable to what I'd been using apply induction for.
Matt Diamond (Aug 01 2025 at 00:45):
if you're calling revert before induction you might want to read up on the generalizing option
JJ (Aug 01 2025 at 00:45):
wow yeah match n with | 0 => ... | m++ => ... is really nice
JJ (Aug 01 2025 at 00:46):
I think this'll go away next chapter! This is using an induction theorem on a definition of Nat that is not the standard one
JJ (Aug 01 2025 at 00:54):
Wow... I can just. leave off branches of the match that lead to contradictions. That's very cool
Aaron Liu (Aug 01 2025 at 00:57):
wait until you hear about recursively calling the theorem from inside itself
JJ (Aug 01 2025 at 03:01):
Is there a nice way to clean this proof up? Specifically, I'm wondering
-
when my goal is0 = 0 ∨ m = 0how to tell Lean "look at the left side"?
tauto -
if there's a way to combine the two matches into one (just using(n,m)with cases(0,0),(0,_),(_,0),(a++,b++)didn't give me thatnormare zero in the first three cases)
yes! justn,m. no parentheses around any match pattern. -
I
apply succ_ne at hto geth : Falseand then follow it bycontradiction-- any way to do it shorter?
lemma Nat.mul_eq_zero (n m: Nat) : n * m = 0 ↔ n = 0 ∨ m = 0 := by
apply Iff.intro
. intro h
match n with
| 0 => simp? -- goal: ⊢ 0 = 0 ∨ m = 0
| a++ =>
match m with
| 0 => simp?
| b++ =>
rw [succ_mul, add_succ] at h
apply succ_ne at h -- after this, h : False
contradiction
. intro h
rcases h with left | right
. rw [left, zero_mul]
. rw [right, mul_zero]
Niels Voss (Aug 01 2025 at 08:14):
For 2., I think you can do something like
match n, m with
| 0, 0 => sorry
| 0, b++ => sorry
| a++, 0 => sorry
| a++, b++ => sorry
JJ (Aug 01 2025 at 08:17):
Oh! Thanks. For some reason I thought you needed ( ). That works nicely.
JJ (Aug 01 2025 at 08:18):
lemma Nat.mul_eq_zero (n m: Nat) : n * m = 0 ↔ n = 0 ∨ m = 0 := by
refine ⟨?_, ?_⟩
. intro h
match n, m with
| 0, _ | _, 0 => tauto
| a++, b++ =>
rw [succ_mul, add_succ] at h
apply succ_ne at h -- after this, h : False
contradiction
. intro h
rcases h with left | right
. rw [left, zero_mul]
. rw [right, mul_zero]
JJ (Aug 01 2025 at 08:19):
cool :)
Niels Voss (Aug 01 2025 at 08:24):
As for your last question, remember that:
False.elimtakes in an element ofFalseand produces anythingexfalsoturns the current goal intoFalseapply succ_ne at hbasically doesreplace h := succ_ne h
So technically, you could replace apply succ_ne at h; contradiction with exact (succ_ne h).elim, although it's debatable whether this is actually "better"
Niels Voss (Aug 01 2025 at 08:27):
You could also do nomatch succ_ne h. This is because False is an inductive type with no constructor, so when you pattern match on it, you actually don't even have to provide any cases.
Matt Diamond (Aug 01 2025 at 17:18):
when my goal is
0 = 0 ∨ m = 0how to tell Lean "look at the left side"?
you listed tauto as the answer (and that will work) but for the more general case of focusing on the left branch of P ∨ Q you can use left
Last updated: Dec 20 2025 at 21:32 UTC