Zulip Chat Archive
Stream: mathlib4
Topic: Define RHS in conv mode
Joachim Breitner (Jul 27 2023 at 21:19):
(Sorry for wrong stream, guess conv
is actually a lean feature, not a mathlib feature)
Given a goal a * b
, I find myself wanting for a concise way of specifying the position of b
(without repeating it), saying that I want to prove it to be equal to c
, then doing the proof of b = c
, and end up with a = c
. Of course the terms can be larger and more deeply nested, possibly in binders.
I can do
conv => right; tactic => { show (_ = c)); proof_of_b_eq_c
and conv
is certainly nice for navigating there. Is there a better way of than tactic => show (_ = c); …
?
If not, would a command like this be a useful addition to conv
? Maybe
conv => right; equals c by
proof_of_b_eq_c
Joachim Breitner (Jul 27 2023 at 21:31):
This works!
syntax (name := equals) "equals" term "by" tacticSeq : conv
macro_rules
| `(conv|equals $rhs:term by $t:tacticSeq) => `(conv|tactic => show (_ = $rhs); . $t )
The only annoying thing left is that after the by
the goal is not yet updated; I have to type skip
or something like that. Is that fixable?
Probably not, this is already a problem with tactic by …
.
Joachim Breitner (Jul 27 2023 at 21:51):
With tacticSeqIndentGt
instead of tacticSeq
, i.e.,
syntax (name := equals) "equals" term "by" tacticSeqIndentGt : conv
I do get the goal immediately after by
, but it means I have to indent the tactic at least this far:
conv => left; ext k; equals r ^ (n+1) * ((↑n + 1) * r^k + k * r ^ k) by
ring
Maybe ok, although I would like to be able two write
conv => left; ext k; equals r ^ (n+1) * ((↑n + 1) * r^k + k * r ^ k) by
ring
Mario Carneiro (Jul 28 2023 at 00:30):
that's not really approved style, you shouldn't hang a by
after a one-line conv block because it's not one-line anymore
Mario Carneiro (Jul 28 2023 at 00:31):
prefer
conv =>
left; ext k
equals r ^ (n+1) * ((↑n + 1) * r^k + k * r ^ k) by
ring
Joachim Breitner (Jul 28 2023 at 06:32):
I see, thanks! In that style I can even use tacticSeqIdentGt
and get the right goal state, which is good.
So maybe if I don't want to get rid of the single-line aesthetics, I should write a macro that takes the convSeq
as well…
Joachim Breitner (Jul 28 2023 at 08:02):
I’m trying
syntax (name := cchange)
"cchange" Lean.Parser.Tactic.Conv.convSeq " with " term " => " tacticSeq: tactic
macro_rules
| `(tactic|cchange $conv:convSeq with $rhs:term => $t:tacticSeq) =>
`(tactic|conv => { $conv; tactic => { show (_ = $rhs); . $t}; done } )
but lean doesn’t like that: $conv
is a convSeq
, but it expects a conv
at this point.
Is there a way to append two convSeq
(the one given by the user, and the tactic… ; done
) in such rules?
Kyle Miller (Jul 28 2023 at 08:04):
You can do ($conv)
instead of $conv
to turn the sequence into a single conv
.
Kyle Miller (Jul 28 2023 at 08:05):
Parentheses can be used for both tactic
and conv
sequences
Peter Pfaffelhuber (Jul 28 2023 at 08:07):
(deleted)
Joachim Breitner (Jul 28 2023 at 08:14):
Thanks! I think tried that, cargo-culting it from somewhere else, but then something else didn’t work… let me have a look again.
With
syntax (name := cchange)
"cchange" Lean.Parser.Tactic.Conv.convSeq " with " term " => " tacticSeq: tactic
macro_rules
| `(tactic|cchange $conv:convSeq with $rhs:term => $t:tacticSeq) =>
`(tactic|conv => { ($conv); tactic => { show (_ = $rhs); ($t)}; done } )
example : 1 = 2 + 3 := by
cchange { right; right } with 4 => { sorry }
I get “no goals to be solved” where the sorry
is.
(This isn’t super important, I am just playing around with the goal of understanding the machinery better.)
Kyle Miller (Jul 28 2023 at 08:16):
Watch out, { right; right }
means (right; right; try rfl)
here. Curly braces focus on the first goal and then try rfl
at the end to auto-close.
Kyle Miller (Jul 28 2023 at 08:17):
You can do cchange right; right with 4 => { }
or cchange (right; right) with 4 => { }
to mean what you expect
Joachim Breitner (Jul 28 2023 at 08:17):
Ah, indeed, and it’s even prettier. Thanks!
Based on
syntax convSeq := convSeqBracketed <|> convSeq1Indented
I was somehow expecting to have to use brackets or indentation there.
Joachim Breitner (Jul 28 2023 at 08:39):
Is there a nice trick to make a multi-argument macro work nicely even if it is typed partiall? For example, so that after
cchange right;
I see the conv
state, even before I write with
and by
?
This comes close, not sure if that’s the way to go, though.
syntax (name := cchange)
"cchange" Lean.Parser.Tactic.Conv.convSeq (" with " (term (" by " tacticSeq)?)?)? : tactic
macro_rules
| `(tactic|cchange $conv:convSeq with $rhs:term by $t:tacticSeq) =>
`(tactic|conv => { ($conv); tactic => { show (_ = $rhs); ($t)}; done } )
| `(tactic|cchange $conv:convSeq with $rhs:term ) =>
`(tactic|conv => { ($conv); tactic => { show (_ = $rhs) }; done } )
| `(tactic|cchange $conv:convSeq with) =>
`(tactic|conv => $conv )
| `(tactic|cchange $conv:convSeq) =>
`(tactic|conv => $conv )
Sebastian Ullrich (Jul 28 2023 at 08:55):
I don't think that's something we've tackled so far. Most of our tactics have an independent meaning without the suffixes
Joachim Breitner (Jul 28 2023 at 09:51):
Fair. Smaller composable tactics are nicer anyways, so I'll stick to equals
maybe
Kyle Miller (Jul 28 2023 at 10:07):
I like this equals
tactic idea. I wonder if the syntax could be equals term => tacticSeq
to better match tactic
Maybe this could go into Std.Tactic.Basic
?
Joachim Breitner (Jul 28 2023 at 22:36):
I think I went back and forth between by
and =>
half a dozen times yesterday :-)
I lean towards by
, because that’s familiar as “opens a block of tactics to prove something”. I find it odd that tactic
doesn’t use it, but maybe because it doesn’t always close a goal?
If you think that this is useful I’ll happily PR it into Std.Tactic.Basic
or where.
I think it is a very natural way of interactive proving. Stare at the goal, notice a subexpression where you can make progress, focus, say what you want to rewrite it to, prove the rewriting, all with DRY.
It’s basically the less pretty; less verbose style version of a nice calc
proof, without the repetition and the := by congr 3; …
before the actual proof steps.
Or the equivalent of the pen-and-paper style of putting curly braces under some subexpression to state that it’s equal to something else.
I found it useful in a lemma dealing with series. If I try to do it with cacl
and ∑' _ = _
, then I have all the Summable
side conditions, and have to show some transformations twice. But if I state the result using HasSum _ _
the summability is part of the goal, and I can use this tactic to freely shuffle things under the sum, or in the value of the sum, without dealing with side conditions.
Joachim Breitner (Jul 29 2023 at 08:26):
PR at https://github.com/leanprover/std4/pull/204, happy to refine there
Last updated: Dec 20 2023 at 11:08 UTC