Zulip Chat Archive

Stream: lean4

Topic: precedence of `;`/`<;>` vs `try`/`repeat`


David Renshaw (Dec 08 2022 at 14:46):

I find this behavior surprising:

variable {α : Type} (a b c : α)

-- works
example (h₁ : a = b) (h₂ : c = b) : b = a :=
by try rw[h₂]
   rw[h₁]

-- does not work
example (h₁ : a = b) (h₂ : c = b) : b = a :=
by try rw[h₂]; rw[h₁]
   -- goal not closed yet
   sorry

-- works with parentheses
example (h₁ : a = b) (h₂ : c = b) : b = a :=
by (try rw[h₂]); rw[h₁]

This came up in https://github.com/leanprover-community/mathlib4/pull/914, and earlier in https://github.com/leanprover-community/mathlib4/pull/370#discussion_r949498757.

Have other people hit trouble with this? Should I open an issue on the lean4 repo?

Mario Carneiro (Dec 08 2022 at 14:48):

While I agree with the sentiment, I'm not sure what the better alternative is, because try takes a tactic block, which can be indented and have internal semicolons, and it's not clear where to make it stop

David Renshaw (Dec 08 2022 at 14:48):

It's especially confusing, because the current relative precedences are different from how they are in Lean 3, so we're bound to keep running into it during the port

David Renshaw (Dec 08 2022 at 14:49):

(though I suppose we can ask mathport to insert parens)

Mario Carneiro (Dec 08 2022 at 14:49):

note that in lean 3 there was explicit bracketing

David Renshaw (Dec 08 2022 at 14:49):

what do you mean by that?

Mario Carneiro (Dec 08 2022 at 14:49):

which we could also do, try { tac } is legal syntax and does what you want

Mario Carneiro (Dec 08 2022 at 14:51):

example (h₁ : a = b) (h₂ : c = b) : b = a :=
by try {rw[h₂]}; rw[h₁] -- works

Mario Carneiro (Dec 08 2022 at 14:52):

mathport should currently be adding parentheses iff they are necessary. Are you saying that it is producing incorrect code?

David Renshaw (Dec 08 2022 at 14:55):

https://github.com/leanprover-community/mathlib4/pull/700 added a comment with syntax that did not include the parens:

    -- This used to be almost automatic with `split_ifs` and `cc`
    -- The split_ifs regression is noted in https://github.com/leanprover-community/mathlib4/issues/760
    -- split_ifs  at h <;> try subst b <;> try simp only [f.injective.eq_iff] at * <;> cc⟩

I was assuming that this came from the mathport output.

David Renshaw (Dec 08 2022 at 14:57):

It's possible that it was just manually copied / imperfectly ported.

Sebastian Ullrich (Dec 08 2022 at 14:57):

This has come up before and I think the best compromise we could come up with is that try etc on a single line should accept only a single tactic (with high precedence), while try followed by a linebreak should continue to work as right now

Mario Carneiro (Dec 08 2022 at 15:05):

The current parenthesizer is indeed broken:

import Lean
open Lean PrettyPrinter

#eval show MetaM _ from do
  let tac  `(tactic| try skip)
  let stx  `(tactic| ($tac; skip))
  formatTactic ( parenthesizeTactic stx)
-- (try skip; skip)

Mario Carneiro (Dec 08 2022 at 15:06):

(the correct answer there is ((try skip); skip))

Sebastian Ullrich (Dec 08 2022 at 15:09):

The fix is the same: ; should have a lower precedence than try. Currently it doesn't have any precedence at all in any sense, so the parenthesizer is powerless to do anything.

Mario Carneiro (Dec 08 2022 at 15:10):

the parenthesizer shouldn't be powerless here, even though it doesn't have a precedence in the usual sense

Mario Carneiro (Dec 08 2022 at 15:11):

Any issues of try parsing too much (under the current semantics) can be fixed with appropriately located parentheses, so it does seem like a job for the parenthesizer

Sebastian Ullrich (Dec 08 2022 at 15:11):

Precedence in the usual sense is all the current parenthesizer knows about

Mario Carneiro (Dec 08 2022 at 15:13):

okay, but this does seem to be part of the job description of the parenthesizer even if it isn't represented nicely for it

Mario Carneiro (Dec 08 2022 at 15:13):

I recall there being some text in the module doc about how the general problem is impossible to solve

David Renshaw (Dec 08 2022 at 15:13):

This has come up before and I think the best compromise we could come up with is that try etc on a single line should accept only a single tactic (with high precedence), while try followed by a linebreak should continue to work as right now

I'm worried that it would be confusing for these to have different meaning:

 try A
     B
 try
   A
   B

Mario Carneiro (Dec 08 2022 at 15:14):

those would both mean try (A; B)

Sebastian Ullrich (Dec 08 2022 at 15:15):

Yes, I think my description was more restrictive than a sensible implementation would be

Mario Carneiro (Dec 08 2022 at 15:16):

the counterintuitive behavior in sebastian's proposal is that

try A; B

means (try A); B but

try A; B
    C

means try (A; B; C)

Mario Carneiro (Dec 08 2022 at 15:18):

Here's a much simpler option: try takes a tactic, try => takes a tactic block

Sebastian Ullrich (Dec 08 2022 at 15:19):

A precedence ordering implementation should lead to

((try A); B)
     C

I believe, which would be a parse error, but I think I like your proposal more!

Mario Carneiro (Dec 08 2022 at 15:21):

what's the input? ((try A); B)\n C?

Mario Carneiro (Dec 08 2022 at 15:22):

I'm not sure why the C is hanging there

Sebastian Ullrich (Dec 08 2022 at 15:24):

The first line should be the partial syntax tree to your input, then the indented C is a parse error because of the checkColEq in sepBy1Indent

Sebastian Ullrich (Dec 08 2022 at 15:25):

with the precedence ordering ; < try < \n

Sebastian Ullrich (Dec 08 2022 at 15:26):

Ah, but we also want \n<;... yeah, that doesn't work

Sebastian Ullrich (Dec 08 2022 at 15:31):

Where's that paper about non-transitive precedence relations... :)

David Renshaw (Feb 23 2023 at 14:43):

Sebastian Ullrich said:

This has come up before and I think the best compromise we could come up with is that try etc on a single line should accept only a single tactic (with high precedence), while try followed by a linebreak should continue to work as right now

I wonder if instead we could introduce some lightweight (non-whitespace) syntax to mark the "now block" case. Like, maybe try: with a colon means "open a new block".

Sebastian Ullrich (Feb 23 2023 at 15:02):

Yes, that sounds similar to
Mario Carneiro said:

Here's a much simpler option: try takes a tactic, try => takes a tactic block

We probably don't want both => and :

Sebastian Ullrich (Feb 23 2023 at 15:03):

Combinators like case that already have => would presumably not have an inline version, which should be o.k.


Last updated: Dec 20 2023 at 11:08 UTC