Zulip Chat Archive
Stream: mathlib4
Topic: sorry a tactic block
Kyle Miller (Mar 17 2024 at 19:00):
In Lean 3 we added the feature that you could sorry
out subproofs by putting sorry
in front of them. It worked particularly well because subproofs were wrapped in curly braces, so you could just put the sorry
in front of the leading {
. For example, you could replace { intro, expensive_tactic, }
with sorry { intro, expensive_tactic, }
, even if the subproof spanned multiple lines. https://github.com/leanprover-community/lean/pull/689
I've been wondering what a Lean 4 version could look like for a while.
This seems like a design:
"with_sorry" tactic
evaluates assorry
(ignoring the tactic)"with_sorry" " => " tacticSeq
evaluates assorry
(ignoring the tactic sequence)
This way you can "comment out" the rest of a block by inserting with_sorry =>
(you can also adjust the indentation for that if needed).
You can also "comment out" subproofs with with_sorry
, for example, the with_sorry
would consume the existing ·
block in the following:
theorem perm_factors_mul {a b : ℕ} (ha : a ≠ 0) (hb : b ≠ 0) :
(a * b).factors ~ a.factors ++ b.factors := by
refine' (factors_unique _ _).symm
· rw [List.prod_append, prod_factors ha, prod_factors hb]
with_sorry
· intro p hp
rw [List.mem_append] at hp
cases' hp with hp' hp' <;> exact prime_of_mem_factors hp'
The main goal with this design for with_sorry
is that it is easy to insert without adjusting the formatting of the rest of the proof.
Yaël Dillies (Mar 17 2024 at 19:08):
Can it be similar to how classical
works?
Kyle Miller (Mar 17 2024 at 19:10):
Sure, it could, but I'm not sure that classical
is how we would want this to work.
Kyle Miller (Mar 17 2024 at 19:11):
I think often you want to comment out a single sub-proof, rather than comment out the rest of the proof.
Mario Carneiro (Mar 17 2024 at 19:11):
Kyle Miller said:
In Lean 3 we added the feature that you could
sorry
out subproofs by puttingsorry
in front of them. It worked particularly well because subproofs were wrapped in curly braces, so you could just put thesorry
in front of the leading{
. For example, you could replace{ intro, expensive_tactic, }
withsorry { intro, expensive_tactic, }
, even if the subproof spanned multiple lines. https://github.com/leanprover-community/lean/pull/689I've been wondering what a Lean 4 version could look like for a while.
Are you familiar with the stop
tactic? In mathport I originally used sorry =>
as the spelling of lean 3 sorry { ... }
but later switched it to stop
which is not different enough to matter
Mario Carneiro (Mar 17 2024 at 19:12):
(More generally, if you are thinking "what happened to that old lean 3 feature?" then mathport has probably already considered it and come up with an answer)
Mario Carneiro (Mar 17 2024 at 19:14):
stop
is a complete drop-in replacement for with_sorry
in your examples
Thomas Murrills (Mar 17 2024 at 19:16):
Kyle Miller said:
I think often you want to comment out a single sub-proof, rather than comment out the rest of the proof.
I’m not sure how stop
works, but it certainly sounds like it doesn’t do this; if it does, maybe it could use a better name
Kyle Miller (Mar 17 2024 at 19:17):
I'd seen (but forgotten) about it, but I think it's worth thinking about how the user experience should work.
It's a "drop-in replacement" functionally, but not experience-wise. It doesn't solve the problem of "how do I conveniently sorry-out this single focus block". The stop
tactic comments out the entire rest of the proof.
Mario Carneiro (Mar 17 2024 at 19:18):
that's the "not quite the same" part, related to the way that block tactics parse in lean 4. In this example stop
will consume both subproofs:
example : True ∧ True ∧ True := by
refine ⟨?_, ?_, ?_⟩
· trivial
stop
· trivial
· trivial
Mario Carneiro (Mar 17 2024 at 19:18):
this is also why stop
is defined as repeat sorry
instead of just sorry
, because otherwise it would bracket both subproofs but only close the first goal
Kyle Miller (Mar 17 2024 at 19:18):
Exactly, so with what I was proposing, there would be a way to consume just the next tactic, which I think is a very important feature as a replacement for sorry { ... }
Mario Carneiro (Mar 17 2024 at 19:19):
You need some indentation if you want to only consume the one goal
Mario Carneiro (Mar 17 2024 at 19:19):
But actually it's not that bad, you can always kill individual subproofs by just moving the stop
:
example : True ∧ True ∧ True := by
refine ⟨?_, ?_, ?_⟩
· trivial
· stop
trivial
· trivial
Kyle Miller (Mar 17 2024 at 19:20):
Is there some mistake with this design that I proposed that would make it so that you need some indentation to consume one goal?
Kyle Miller said:
This seems like a design:
"with_sorry" tactic
evaluates assorry
(ignoring the tactic)"with_sorry" " => " tacticSeq
evaluates assorry
(ignoring the tactic sequence)
Kyle Miller (Mar 17 2024 at 19:20):
Mario Carneiro said:
But actually it's not that bad, you can always kill individual subproofs by just moving the
stop
:
I discarded this idea myself, because you have to alter that subproof by inserting the right amount of whitespace
Mario Carneiro (Mar 17 2024 at 19:21):
like I said, that's exactly how the sorry =>
tactic worked. We could bring it back but it has a lot of overlap with stop
Mario Carneiro (Mar 17 2024 at 19:22):
Also with_sorry foo; bar
will have some weird parsing in your example
Kyle Miller (Mar 17 2024 at 19:22):
At least you can do with_sorry => foo; bar
if you notice the weird parsing.
Thomas Murrills (Mar 17 2024 at 19:22):
sorry =>
does look like it demands indentation after it, though, even if it doesn’t.
Mario Carneiro (Mar 17 2024 at 19:23):
The idea is that it really should have indentation
Mario Carneiro (Mar 17 2024 at 19:23):
if you want the unindented block version you should probably be using stop
Thomas Murrills (Mar 17 2024 at 19:23):
I think Kyle’s idea is that you should be able to insert it in a proof easily for good UX
Kyle Miller (Mar 17 2024 at 19:24):
And there's the consideration that these are never meant to be permanent parts of the proofs, so you want to be able to insert/delete them quickly and accurately
Mario Carneiro (Mar 17 2024 at 19:24):
If the whitespace handling around the dots is annoying you can always just put an extra newline there
Mario Carneiro (Mar 17 2024 at 19:25):
I agree that this is an issue, although it's true in general and not just with stop
Mario Carneiro (Mar 17 2024 at 19:25):
it would be great if vscode autoindent played better with the dots
Kyle Miller (Mar 17 2024 at 19:25):
Are you saying that we could "just" make all of mathlib look like this?
theorem perm_factors_mul_of_coprime {a b : ℕ} (hab : Coprime a b) :
(a * b).factors ~ a.factors ++ b.factors := by
rcases a.eq_zero_or_pos with (rfl | ha)
·
simp [(coprime_zero_left _).mp hab]
rcases b.eq_zero_or_pos with (rfl | hb)
·
simp [(coprime_zero_right _).mp hab]
exact perm_factors_mul ha.ne' hb.ne'
Kyle Miller (Mar 17 2024 at 19:26):
Just to support being able to insert stop
?
Mario Carneiro (Mar 17 2024 at 19:26):
no, I'm saying that you would reformat into that style on an ad-hoc basis when messing about
Mario Carneiro (Mar 17 2024 at 19:27):
vscode doesn't have the same issues with markdown lists, so maybe there is a way to fix it in the editor
Kyle Miller (Mar 17 2024 at 19:27):
I agree that is something that we could do, but I think you have to agree that as a user experience, that's a non-starter, unless we want to rely on editor support.
Thomas Murrills (Mar 17 2024 at 19:27):
Mario Carneiro said:
it would be great if vscode autoindent played better with the dots
I think the extension PR I made for this a while ago recently got merged
Mario Carneiro (Mar 17 2024 at 19:27):
I mean it's not that bad to type stop + enter + tab
but I get what you mean
Kyle Miller (Mar 17 2024 at 19:27):
I would rather not rely on editor support. There are solutions where you can "just" insert some tactics (like the ones I proposed) and get the behavior that you want.
Yaël Dillies (Mar 17 2024 at 19:28):
My muscles expect
sorry · tactic_that_does_not_run
another_tactic_not_running
to work
Kyle Miller (Mar 17 2024 at 19:28):
("Just" is in quotes because I usually consider it to be a banned word for design discussions. It always hides too much about what the underlying constraints are, and what we each think is important to design for.)
Mario Carneiro (Mar 17 2024 at 19:29):
stop
would almost work with that, but you broke the whitespace sensitive tacticSeq there
Yaël Dillies (Mar 17 2024 at 19:29):
I think that's easy enough to be user-friendly, and ugly enough that nobody will want to leave that in final code?
Yaël Dillies (Mar 17 2024 at 19:29):
Yeah; that breaks the tacticSeq
expectation, but I would claim that's a feature?
Kyle Miller (Mar 17 2024 at 19:29):
I'd want your idea to work @Yaël Dillies, but I think it's not technically possible because of indentation sensitivity (what Mario said). What if you want to comment out a tactic that itself is multi-line and indentation sensitive?
Thomas Murrills (Mar 17 2024 at 19:30):
Half-serious proposal for an alternate name for with_sorry
if Yaël’s sorry
doesn’t work (but I like that one best): sorry_about
(pros: initial sorry
so you can easily see when a line starts with it, makes “linguistic sense”; cons: not idiomatic/consistent with other with
s)
Kyle Miller (Mar 17 2024 at 19:31):
(I do want to mention that I think stop
is a good design for stopping a proof partway through.)
Mario Carneiro (Mar 17 2024 at 19:31):
For the case of stubbing out a single subgoal from the outside, kyle's with_sorry
would work. I wouldn't recommend using it in any other situation though
example : True ∧ True ∧ True := by
refine ⟨?_, ?_, ?_⟩
· trivial
with_sorry
· trivial
· trivial
Yaël Dillies (Mar 17 2024 at 19:32):
Can't macros handle multiline things somehow? I mean if we have a macro that literally replaces
sorry · tac1
tac2
with
· stop
tac1
tac2
then we win, right?
Kyle Miller (Mar 17 2024 at 19:32):
Maybe the solution is add with_sorry
for commenting out a single tactic, and keep stop
as-is?
Mario Carneiro (Mar 17 2024 at 19:32):
it's already a parse error though @Yaël Dillies
Thomas Murrills (Mar 17 2024 at 19:32):
What if we had "sorry" "."
<followed by appropriate tactics> parse?
Mario Carneiro (Mar 17 2024 at 19:33):
but you could have a code action that changes
· tac1
tac2
to
· stop
tac1
tac2
Mario Carneiro (Mar 17 2024 at 19:33):
maybe just "insert tactic here"
Yaël Dillies (Mar 17 2024 at 19:33):
Hmm, I thought we could have macro-like objects getting replaced at several points, including possibly before parsing?
Mario Carneiro (Mar 17 2024 at 19:34):
In fact, the tactic code actions infrastructure was designed specifically to support code actions of this form
Mario Carneiro (Mar 17 2024 at 19:35):
Yaël Dillies said:
Hmm, I thought we could have macro-like objects getting replaced at several points, including possibly before parsing?
The trouble is that if it doesn't parse then either you have no parse information or garbage parse information, so either way it's difficult to make a very intelligent suggestion about the code
Thomas Murrills (Mar 17 2024 at 19:36):
(On the code action route, there also ought to be a code action to remove stop
, which could also be annoying to do)
Mario Carneiro (Mar 17 2024 at 19:37):
ditto for done
Mario Carneiro (Mar 17 2024 at 19:37):
this probably overlaps with (the code action for) @Damiano Testa 's useless tactic linter
Kyle Miller (Mar 17 2024 at 19:37):
Hey, his tactic linter is useful :upside_down:
Damiano Testa (Mar 17 2024 at 19:39):
The useless tactic linter does not have a code action... at least not yet! I think that finding a way to formatting the output might be the biggest blocker, though.
Thomas Murrills (Mar 17 2024 at 19:39):
By the way, can you still see the tactic state at a tactic that comes after stop
? Say, if you wanted to consider whether to keep working on it?
Mario Carneiro (Mar 17 2024 at 19:40):
rust-analyzer has a code action for remove dbg!()
where you can just select a whole code block and it will delete all these temporary debugging things. We could do the same for stop
/ done
removal
Mario Carneiro (Mar 17 2024 at 19:40):
@Thomas Murrills No? I mean the code doesn't run at all
Kyle Miller (Mar 17 2024 at 19:40):
I'm liking this design for "how to sorry out proofs":
- keep
stop
exactly as it is, to sorry out the rest of a proof with_sorry
(or whatever the name) for a single tactic, to support sorrying out blocks without relying on editor support- create a code action for inserting and removing other tactics, including
stop
Kyle Miller (Mar 17 2024 at 19:40):
Maybe inserting skip
before/after would be a good code action to have?
Mario Carneiro (Mar 17 2024 at 19:40):
@Thomas Murrills I'm not sure how that could work
Yaël Dillies (Mar 17 2024 at 19:41):
How do code actions display? Are they "noisy", as in add an info message to the infoview?
Mario Carneiro (Mar 17 2024 at 19:41):
they are the lightbulb
Thomas Murrills (Mar 17 2024 at 19:41):
It could work if syntax like sorry . <tacs>
worked (I.e. if sorry .
was like .
)
Yaël Dillies (Mar 17 2024 at 19:42):
Thomas, you can see the tactic state by putting your cursor here: |stop
Thomas Murrills (Mar 17 2024 at 19:42):
I mean at the end of the block, not the beginning!
Mario Carneiro (Mar 17 2024 at 19:42):
Thomas Murrills said:
It could work if syntax like
sorry . <tacs>
worked (I.e. ifsorry .
was like.
)
This already works, exactly as Yael described (for stop
). The trouble is that you have to match indentation for the rest of the block
Thomas Murrills (Mar 17 2024 at 19:42):
Oh, so the code does run!
Mario Carneiro (Mar 17 2024 at 19:44):
i.e.
stop · tac1
tac2
is a parse error but
stop · tac1
tac2
parses as intended (although actually you wouldn't want to use stop
for this because it does repeat sorry
instead of sorry
)
Mario Carneiro (Mar 17 2024 at 19:44):
it's just that aligning the rest of the block is way too much work to ask for
Damiano Testa (Mar 17 2024 at 19:45):
You could do this and maybe get Yaël's behaviour:
syntax "sorrys " tactic (colGt tacticSeq) : tactic
open Lean Elab Command Tactic
elab_rules : tactic
| `(tactic| sorrys $_
$_) => do evalTactic (← `(tactic| sorry))
example (n : Nat) : True := by
rcases n with _|_|_|_|_
sorrys · have := 0
have := 1
repeat exact .intro
Thomas Murrills (Mar 17 2024 at 19:45):
I just want to be clear: if you write
. stop
tac1
tac2
then placing your cursor before stop shows the tactic state after tac2? That’s what I’m asking about.
Mario Carneiro (Mar 17 2024 at 19:46):
No, it shows the tactic state before tac1
Mario Carneiro (Mar 17 2024 at 19:46):
and there is no tactic state after tac1 because tac1 was never run
Mario Carneiro (Mar 17 2024 at 19:47):
if you put your cursor inside or after the stop
token or anywhere in the block I expect you to see No goals
because stop
closes all goals
Thomas Murrills (Mar 17 2024 at 19:47):
Mario Carneiro said:
Thomas Murrills said:
It could work if syntax like
sorry . <tacs>
worked (I.e. ifsorry .
was like.
)This already works, exactly as Yael described (for
stop
). The trouble is that you have to match indentation for the rest of the block
Ok, I got confused by “this already works”—I thought you were talking about the behavior I’m asking about
Mario Carneiro (Mar 17 2024 at 19:48):
? I am?
Mario Carneiro (Mar 17 2024 at 19:48):
if <tacs>
was on one line it would work
Mario Carneiro (Mar 17 2024 at 19:49):
@Damiano Testa that fails if the first line has more than one tactic in it (separated by ;
)
Damiano Testa (Mar 17 2024 at 19:49):
I'm not sure it does: this seems to work for me
example (n : Nat) : True := by
rcases n with _|_|_|_|_
sorrys· have := 0; have := 1
exact .intro
repeat exact .intro
Mario Carneiro (Mar 17 2024 at 19:49):
I wonder what would happen if you use "sorrys " tacticSeq (colGt tacticSeq)?
though
Mario Carneiro (Mar 17 2024 at 19:51):
it's because you used a dot after sorrys
Mario Carneiro (Mar 17 2024 at 19:51):
sorrys have := 0; have := 0
have := 1
doesn't parse
Damiano Testa (Mar 17 2024 at 19:51):
Mario Carneiro said:
it's because you used a dot after
sorrys
Right, but this was Yaël's suggestion. In the situation that you are describing, wouldn't stop already work?
Thomas Murrills (Mar 17 2024 at 19:51):
stop . tac1
tac2
runs tac1
, tac2
on the goal after stop
, does it not?
Thomas Murrills (Mar 17 2024 at 19:52):
(Sorry, this is a reply to Mario.)
Mario Carneiro (Mar 17 2024 at 19:52):
No
Damiano Testa (Mar 17 2024 at 19:53):
You are right, but your suggestion works, I think.
Mario Carneiro said:
I wonder what would happen if you use
"sorrys " tacticSeq (colGt tacticSeq)?
though
Mario Carneiro (Mar 17 2024 at 19:53):
it does not run tac1 or tac2, but if there is a tac3
after it then it would be run
stop . tac1
tac2
. tac3
tac4
this runs tac3
and tac4
but not tac1
and tac2
(and fails at the second dot because of the semantics of stop
)
Damiano Testa (Mar 17 2024 at 19:54):
So, this is what might work:
syntax "sorrys " tacticSeq (colGt tacticSeq)? : tactic
open Lean Elab Command Tactic
elab_rules : tactic
| `(tactic| sorrys $_
$_) => do evalTactic (← `(tactic| sorry))
example (n : Nat) : True := by
rcases n with _|_|_|_|_
sorrys have := 0; have := 1
exact .intro
sorrys have := 0
have := 1
exact .intro
repeat exact .intro
Thomas Murrills (Mar 17 2024 at 19:54):
Mario Carneiro said:
No
Ok, so it must not produce the behavior I’m talking about, right? Because it doesn’t run tac1
/tac2
? I’m specifically looking to get the tactic state at tac2
.
Yaël Dillies (Mar 17 2024 at 19:55):
Yeah, Mario is misunderstanding what behavior you were talking about
Notification Bot (Mar 17 2024 at 19:55):
95 messages were moved here from #mathlib4 > haves with blanks by Mario Carneiro.
Yaël Dillies (Mar 17 2024 at 19:56):
The point of stop
is to not run the tactics that are in the same indentation block, because they are expensive, throw a lot of error, etc...
Mario Carneiro (Mar 17 2024 at 19:56):
Thomas Murrills said:
Mario Carneiro said:
No
Ok, so it must not produce the behavior I’m talking about, right? Because it doesn’t run
tac1
/tac2
? I’m specifically looking to get the tactic state attac2
.
It's impossible to get the tactic state at tac2
without running tac1
Mario Carneiro (Mar 17 2024 at 19:56):
It would be great if there was a "sorry mode" one could set in the tactic state so that tactics only do structural stuff and sorry everything they can...
Mario Carneiro (Mar 17 2024 at 19:57):
then you could get the state of tac2
without "fully" running tac1
Mario Carneiro (Mar 17 2024 at 19:57):
this would be especially useful for simp
Thomas Murrills (Mar 17 2024 at 20:11):
Yaël Dillies said:
The point of
stop
is to not run the tactics that are in the same indentation block, because they are expensive, throw a lot of error, etc...
Hmm, yeah, makes sense...thinking about it more, waiting for a stop
ped proof to elaborate when it has expensive tactics just to have the option of seeing how far you've gotten seems like it would be too annoying...
Thomas Murrills (Mar 17 2024 at 20:13):
I suppose second-best to being able to see the tactic state after tac2
is to be able to activate the stop
-removing code action from anywhere after stop
! :) (Which is maybe already what you had in mind, Mario?)
Thomas Murrills (Mar 17 2024 at 20:23):
Mario Carneiro said:
it would be great if vscode autoindent played better with the dots
(Just to record this here now that I’m at my computer, this was vscode-lean4#328, merged 4 days ago)
Matthew Ballard (Mar 18 2024 at 14:56):
(deleted)
Matthew Ballard (Mar 18 2024 at 15:15):
:peach: post
Last updated: May 02 2025 at 03:31 UTC