Zulip Chat Archive

Stream: lean4

Topic: when does a branch in the first combinator change the goal?


Bulhwi Cha (Apr 03 2025 at 13:00):

Consider the following example from Section Tactic Combinators of #tpil:

example (p q r : Prop) (hr : r) : p  q  r := by
  repeat (first | apply Or.inl; assumption | apply Or.inr | assumption)

The compound tactic first tries to solve the left disjunct, p, immediately by assumption. That attempt fails, and the goal remains the same. It then tries to focus on the right disjunct, q ∨ r. That attempt also fails; however, the goal changes from p ∨ q ∨ r to q ∨ r.

When exactly does a subtactic in the first combinator change the goal?

Jovan Gerbscheid (Apr 03 2025 at 13:08):

Did you mean to write
repeat (first | apply Or.inl; assumption | apply Or.inr; assumption)

Bulhwi Cha (Apr 03 2025 at 13:12):

No, that doesn't work.

Jovan Gerbscheid (Apr 03 2025 at 13:14):

It seems to match what you were describing. You say that the attempt to focus on the right goal fails. But the focussing on the right goal of course doesn't fail.

Jovan Gerbscheid (Apr 03 2025 at 13:14):

As in, apply Or.inr is successful, so that is were the first combinator stops

Bulhwi Cha (Apr 03 2025 at 13:15):

I'm not sure what the word "fail" really means in this context.

Jovan Gerbscheid (Apr 03 2025 at 13:15):

fail means that the tactic throws an error

Jovan Gerbscheid (Apr 03 2025 at 13:16):

apply Or.inl; assumption throws an error because assumption does. apply Or.inr doesn't throw an error

Bulhwi Cha (Apr 03 2025 at 13:16):

From docs#Lean.Parser.Tactic.first:

def Lean.Parser.Tactic.first : ParserDescr

first | tac | ... runs each tac until one succeeds, or else fails.

Jovan Gerbscheid (Apr 03 2025 at 13:17):

Oh, is the "runs each tac" confusing? It doesn't actually run each tactic: it stops when one succeeds.

Bulhwi Cha (Apr 03 2025 at 13:28):

Jovan Gerbscheid said:

As in, apply Or.inr is successful, so that is were the first combinator stops

Does apply Or.inr succeed in the following example?

example (p q r : Prop) (hr : r) : p  q  r := by
  apply Or.inr

Bulhwi Cha (Apr 03 2025 at 13:29):

What exactly is the meaning of the word "succeed" in this context?

Jovan Gerbscheid (Apr 03 2025 at 13:29):

Yes, it succeeds because the tactic doesn't throw an error

Jovan Gerbscheid (Apr 03 2025 at 13:31):

Here is an example of a tactic that doesn't succeed:

example (p q r : Prop) (hr : r) : p  q  r := by
  apply trivial

Bulhwi Cha (Apr 03 2025 at 13:31):

I do get an error message from the first line of my example:

Messages here:
1:47:
unsolved goals
case h
p q r : Prop
hr : r
 q  r

Jovan Gerbscheid (Apr 03 2025 at 13:35):

This error message is not thrown by the apply tactic. The apply tactic successfully applies, and then there are no more tactics left, so lean checks if all goals have been closed. That is what throws this error.

For example:

example (p q r : Prop) (hr : r) : p  q  r := by
  apply Or.inr
  repeat sorry

doesn't throw an error (just a warning), while

example (p q r : Prop) (hr : r) : p  q  r := by
  apply trivial
  repeat sorry

throws an error, because apply trivial failed. So Lean doesn't even try to repeat sorry.

Bulhwi Cha (Apr 03 2025 at 13:52):

example (p q r : Prop) (hr : r) : p  q  r := by
  first | apply Or.inr | exact Or.inr (Or.inr hr) -- unsolved goals

You're right. If apply Or.inr in the above first combinator failed, it'd run exact Or.inr (Or.inr hr), but it doesn't.

Bulhwi Cha (Apr 03 2025 at 14:04):

Bulhwi Cha said:

Consider the following example from Section Tactic Combinators of #tpil:

example (p q r : Prop) (hr : r) : p  q  r := by
  repeat (first | apply Or.inl; assumption | apply Or.inr | assumption)

The compound tactic first tries to solve the left disjunct, p, immediately by assumption. That attempt fails, and the goal remains the same. It then tries to focus on the right disjunct, q ∨ r. That attempt also fails; however, the goal changes from p ∨ q ∨ r to q ∨ r.

I should fix the above explanation as follows:

The first combinator tries to solve the left disjunct, p, immediately by assumption. That attempt fails, and the goal remains the same. It then tries to focus on the right disjunct, q ∨ r. That attempt succeeds, so the goal changes from p ∨ q ∨ r to q ∨ r.

The repeat combinator applies the first combinator again. The second subtactic in the first combinator succeeds, so the goal changes from q ∨ r to r.

The repeat combinator applies the first combinator once again. The last subtactic in first combinator, assumption, succeeds and closes the goal.

Therefore, the compound tactic is equivalent to the following sequence of the same first combinators:

example (p q r : Prop) (hr : r) : p  q  r := by
  first | apply Or.inl; assumption | apply Or.inr | assumption -- runs `apply Or.inr`
  first | apply Or.inl; assumption | apply Or.inr | assumption -- runs `apply Or.inr`
  first | apply Or.inl; assumption | apply Or.inr | assumption -- runs `assumption`

Bulhwi Cha (Apr 03 2025 at 14:31):

I'm glad that I asked about the first combinator. Thanks for your answer!

Bulhwi Cha (Apr 04 2025 at 13:39):

From docs#Lean.Parser.Tactic.first:

def Lean.Parser.Tactic.first : ParserDescr

first | tac | ... runs each tac until one succeeds, or else fails.

Should we change the description of the first combinator as follows?

first | tac | ... runs only the first tac that succeeds; it fails if none of them succeeds.

Bulhwi Cha (Apr 04 2025 at 13:59):

Jovan Gerbscheid said:

Oh, is the "runs each tac" confusing? It doesn't actually run each tactic: it stops when one succeeds.

To put it another way, first | tac | ... tries to run each tac until one succeeds, or else fails. I think we should change the description of it.

Jovan Gerbscheid (Apr 04 2025 at 14:32):

Yes, I think both of these options are a better description.

Kevin Buzzard (Apr 04 2025 at 14:36):

I don't think "runs only the first tac that succeeds" is better than "runs each tac until one succeeds"; in fact I think it's strictly worse. What first does is exactly the latter thing (i.e. what it already says it does), not the former thing. I agree that the second clause is better though.


Last updated: May 02 2025 at 03:31 UTC