Zulip Chat Archive
Stream: lean4
Topic: using repeat and all_goals to prove theorems
Bulhwi Cha (Apr 07 2025 at 10:01):
Unlike the first proof of the example below, the second one gives the message, "maximum recursion depth has been reached." I'm not sure why this happens.
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption)
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
repeat (all_goals (first | constructor | assumption))
Aaron Liu (Apr 07 2025 at 10:17):
all_goals
succeeds on no goals, so repeat
is repeatedly applying it to no goals
Bulhwi Cha (Apr 07 2025 at 10:21):
Aaron Liu said:
all_goals
succeeds on no goals
Can you elaborate on this? Shouldn't all_goals
succeed on the goal p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p)
?
Aaron Liu (Apr 07 2025 at 10:24):
All the goals are solved, but repeat
is supposed to keep going until the tactic fails, so it keeps running it on no goals, and it keeps succeeding
Bulhwi Cha (Apr 07 2025 at 10:27):
Then why does the repeat
tactic succeed in the following example?
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
repeat (any_goals (first | constructor | assumption))
Aaron Liu (Apr 07 2025 at 10:30):
Clearly, my diagnosis was wrong, and the recursion limit is a lot closer than I thought
Bulhwi Cha (Apr 07 2025 at 10:30):
I think I know why:
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption)
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
any_goals (first | constructor | assumption)
any_goals (first | constructor | assumption)
any_goals (first | constructor | assumption)
any_goals (first | constructor | assumption)
any_goals (first | constructor | assumption)
any_goals (first | constructor | assumption) -- failed on all goals
Bulhwi Cha (Apr 07 2025 at 10:32):
I think this behavior of all_goals
is inconsistent with that of any_goals
, but perhaps there's a reason for it. If not, I suggest making all_goals
fail to run when there's no goal to solve.
Yaël Dillies (Apr 07 2025 at 14:00):
Is it inconsistent? ∀ goal : Goal, Solved goal
implies ∃ goal : Goal, Solved goal
only if Goal
is nonempty, after all...
Bulhwi Cha (Apr 07 2025 at 14:16):
I think the meanings of "any" and "exists" are slightly different here. any_goals
applies its tactic argument to as many goals as possible.
Yaël Dillies (Apr 07 2025 at 14:18):
Yes, so does all_goals
. Then any_goals
fails if no goal has progressed, all_goals
fails if not all goals have progressed. That feels pretty consistent to me.
Bulhwi Cha (Apr 07 2025 at 14:20):
Yaël Dillies said:
Yes, so does
all_goals
.
No, it doesn't. Unlike any_goals
, all_goals
tries to apply its tactic argument to all open goals even if it will fail on some goals.
Bulhwi Cha (Apr 07 2025 at 14:22):
example (p q r s : Prop) (hp : p) (hq : q) (hr : r) : s ∧ p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
all_goals (first | constructor | assumption)
all_goals (first | constructor | assumption) -- tactic 'assumption' failed
sorry
example (p q r s : Prop) (hp : p) (hq : q) (hr : r) : s ∧ p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
any_goals (first | constructor | assumption)
any_goals (first | constructor | assumption)
sorry
Jovan Gerbscheid (Apr 07 2025 at 14:29):
I agree with @Yaël Dillies that the behaviour is technically consistent with the tactic name: all_goals tac
fails if and only if tac
fails on one of the goals. (so if there are no goals, all_goals tac
succeeds)
Jovan Gerbscheid (Apr 07 2025 at 14:30):
However, it is common for tactics to check that there are no goals left, and for good reason, because it doesn't make much sense to run a tactic when there are no goals left. So there is still an argument for changing the behaviour.
Yaël Dillies (Apr 07 2025 at 14:30):
There's a linter to catch tactics that do nothing. Why not rely on that instead?
Bulhwi Cha (Apr 07 2025 at 14:32):
Jovan Gerbscheid said:
all_goals tac
fails if and only iftac
fails on one of the goals.
@Jovan Gerbscheid @Yaël Dillies Do you think the second assumption
tactic fails in the following proof? You might say, "It's vacuously true that the second tactic succeeds on all goals since there's no goal to solve." Still, we get an error message.
example (p : Prop) (hp : p) : p := by
assumption
assumption -- no goals to be solved
Jovan Gerbscheid (Apr 07 2025 at 14:37):
Bulhwi Cha said:
Do you think the second
assumption
tactic fails in the following proof?example (p : Prop) (hp : p) : p := by assumption assumption -- no goals to be solved
Yes, as indicated by the red squiggly line under assumption
Jovan Gerbscheid (Apr 07 2025 at 14:38):
Yaël Dillies said:
There's a linter to catch tactics that do nothing. Why not rely on that instead?
Because of the original example:
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
repeat (all_goals (first | constructor | assumption))
Jovan Gerbscheid (Apr 07 2025 at 14:39):
Actually, there is a better solution:
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
repeat' (first | constructor | assumption)
Jovan Gerbscheid (Apr 07 2025 at 14:43):
Well, actually in your example
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
repeat (first | constructor | assumption)
also works.
But you can see the difference between repeat
and repeat'
if the tactic is non-finishing:
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
repeat' (first | constructor)
Bulhwi Cha (Apr 07 2025 at 14:45):
Huh, I didn't know repeat'
exists.
Jovan Gerbscheid (Apr 07 2025 at 14:52):
I forgot about it as well, it's not named amazingly. It does the same as repeat
, but goes into all newly created subgoals.
Bulhwi Cha (Apr 07 2025 at 14:58):
I'm puzzled by the two examples below:
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
repeat (all_goals (first | constructor | assumption)) -- maximum recursion depth has been reached
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by
repeat' (all_goals (first | constructor | assumption))
Jovan Gerbscheid (Apr 07 2025 at 15:00):
Neither repeat
nor repeat'
should be used in combination with all_goals
. But if you want to know the details:
repeat (all_goals tac)
loops here because after all goals are solved,all_goals tac
keeps on succeedingrepeat' (all_goals tac)
doesn't loop, becauserepeat'
only runsall_goals tac
on newly created goals. And at some point no new goal are created.
Bulhwi Cha (Apr 07 2025 at 15:02):
From https://lean-lang.org/theorem_proving_in_lean4/tactics.html:
In fact, we can compress the full tactic down to one line:
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) := by repeat (any_goals (first | constructor | assumption))
#tpil has an example of using repeat
and any_goals
, so I thought I could replace any_goals
with all_goals
in that example.
Jovan Gerbscheid (Apr 07 2025 at 15:03):
In fact, repeat' (all_goals tac)
is equivalent to repeat' tac
, because repeat' tac
only runs tac
on sinlge-goal tactic states.
Jovan Gerbscheid (Apr 07 2025 at 15:06):
I think repeat (any_goals tac)
is a less perfomant version of repeat' tac
(but more conceptual, so it works great in the #tpil book)
Jovan Gerbscheid (Apr 07 2025 at 15:07):
Bulhwi Cha said:
#tpil has an example of using
repeat
andany_goals
, so I thought I could replaceany_goals
withall_goals
in that example.
I don't know why you would want to do that.
Bulhwi Cha (Apr 07 2025 at 15:11):
I'm recording videos of me reading the textbook in Korean and commenting on it. I only wanted to say that you could use all_goals
instead of any_goals
in that example, but it turned out I was wrong.
Last updated: May 02 2025 at 03:31 UTC