Zulip Chat Archive
Stream: lean4
Topic: idiom for using calc to rewrite the goal
Joachim Breitner (Mar 01 2024 at 15:17):
I am a big fan of calc
proofs. If the goal is itself an equality (or other relation), I just just start with calc
.
But what if I want to rewrite the goal p1
with calc p1 =… ; _ = p2 := …
to get goal p2
? Like in this example
example (n : Nat) (P : Nat → Prop) (h : P n): P (0 + 1 * n * 1) := by
apply
Eq.mpr <|
calc
P (0 + 1 * n * 1)
_ = P (0 + n * 1) := (congrArg (fun x => P (0 + x * 1)) (Nat.one_mul n))
_ = P (0 + n) := (congrArg (fun x => P (0 + x)) (Nat.mul_one n))
_ = P n := congrArg P (Nat.zero_add n)
exact h
Is there an even nicer idiom than apply Eq.mpr |> calc …
that I can use here?
Floris van Doorn (Mar 01 2024 at 15:29):
I sometimes use have := calc ...
and then you can do rw [this]
afterwards. But that's perhaps just as cumbersome.
Johan Commelin (Mar 01 2024 at 15:35):
Would this be a job for convert_to
+ calc
-block afterwards?
Johan Commelin (Mar 01 2024 at 15:35):
convert_to h
calc ...
Joachim Breitner (Mar 01 2024 at 15:35):
Context: I am prototyping a tactic that will take a proof goal as produced by simp
or rw
and turn it into a small-step calc
block:
example (n : Nat) : 0 + n = 0 + (n * 1) := by
calcify rw [Nat.mul_one, Nat.zero_add]
/--
info: Try this: calc
0 + n
_ = n := (Nat.zero_add n)
_ = 0 + n := (Eq.symm (Nat.zero_add n))
_ = 0 + n * 1 := congrArg (fun _a => 0 + _a) (Eq.symm (Nat.mul_one n))
-/
Joachim Breitner (Mar 01 2024 at 15:37):
hmm, no convert_to
in Core :-)
Maybe the idiom that should work is
conv => calc …
Already I can write
example (n : Nat) (P : Nat → Prop) (h : P n): P (0 + 1 * n * 1) := by
conv => tactic => calc
P (0 + 1 * n * 1)
_ = P (0 + n * 1) := (congrArg (fun x => P (0 + x * 1)) (Nat.one_mul n))
_ = P (0 + n) := (congrArg (fun x => P (0 + x)) (Nat.mul_one n))
_ = P n := congrArg P (Nat.zero_add n)
exact h
but no reason not to lift calc
to a conv tactic like other tactics?
Johan Commelin (Mar 01 2024 at 15:38):
That seems a good idea, yes
Joachim Breitner (Mar 01 2024 at 15:47):
Issue at https://github.com/leanprover/lean4/issues/3557
Andrés Goens (Mar 01 2024 at 15:49):
Joachim Breitner said:
Context: I am prototyping a tactic that will take a proof goal as produced by
simp
orrw
and turn it into a small-stepcalc
block:example (n : Nat) : 0 + n = 0 + (n * 1) := by calcify rw [Nat.mul_one, Nat.zero_add] /-- info: Try this: calc 0 + n _ = n := (Nat.zero_add n) _ = 0 + n := (Eq.symm (Nat.zero_add n)) _ = 0 + n * 1 := congrArg (fun _a => 0 + _a) (Eq.symm (Nat.mul_one n)) -/
oh, that looks really neat! would that live in core or somewhere outside (e.g. std)?
Joachim Breitner (Mar 01 2024 at 15:50):
Probably in my separate repo, labeled “this is an experiment”, for a while :-D
Andrés Goens (Mar 01 2024 at 15:50):
hehe, fair enough
Joachim Breitner (Mar 01 2024 at 15:50):
Although I hope it’ll be easier to use “dev dependencies”, i.e. commands that you don’t plan to commit (so they don’t need to be a dependency) and just want to add to your toolkit. then separate repo doesn’t sound too bad.
Joachim Breitner (Mar 01 2024 at 15:51):
Of course, if it turns out to be reliable and useful, it can be upstreamed. But it’s not on any roadmap as of now.
Andrés Goens (Mar 01 2024 at 15:51):
I'll be looking out for that
Andrés Goens (Mar 01 2024 at 15:56):
just curious, in the context of these experiments: do you think it might make sense to keep the rewrite list as passed to rw
or simp
in a single list and still have the declarative list of rewrites in calc
? Something like (not talking about the concrete syntax here):
example (n : Nat) : 0 + n = 0 + (n * 1) := by
calcify rw [Nat.mul_one, Nat.zero_add]
/--
info: Try this: calc'
0 + n
_ = n
_ = 0 + n
_ = 0 + n * 1 := by rw [Nat.mul_one, Nat.zero_add]
-/
Joachim Breitner (Mar 01 2024 at 15:57):
While I am at it: What are my options to eliminate the congrArg (fun …)
wrappers around these equality proofs?
We have the ▸
macro which does most of that works, and I can write
((Nat.one_mul n).symm ▸ rfl)
instead, but that’s a mouthful. Is there some other syntax?
Andrés Goens (Mar 01 2024 at 16:00):
context for the question above: we prototyped something along those lines for this paper for something a bit stronger than simp
and it would be nice to do something much more full-fledged from it, where the declarative calc proof is easier to read and maintain, but can still be reconstructed with automation
Joachim Breitner (Mar 01 2024 at 16:02):
Hmm, not sure. One good thing about calc
proofs is that each step has its own proof, so that they break individually when you refactor.
If the goal is just to explain the simp, but keeping the simp, then something inspired like from says
might work:
example (n : Nat) : 0 + n = 0 + (n * 1) := by
rw [Nat.mul_one, Nat.zero_add] performs
0 + n
_ = n
_ = 0 + n
_ = 0 + n * 1
Or, turning it around, roughly your syntax. Ok, that might be useful indeed.
At this point, I wonder, is it useful to have this in the code, or would it suffice to be able to point at the proof and see it in the info view or so.
Andrés Goens (Mar 01 2024 at 16:03):
is your primary goal readibility or performance?
Andrés Goens (Mar 01 2024 at 16:04):
Joachim Breitner said:
While I am at it: What are my options to eliminate the
congrArg (fun …)
wrappers around these equality proofs?We have the
▸
macro which does most of that works, and I can write((Nat.one_mul n).symm ▸ rfl)
instead, but that’s a mouthful. Is there some other syntax?
for readibility I guess you could also just use the individual rw
step here, no?
Joachim Breitner (Mar 01 2024 at 16:04):
Experimentation :-D
And next explanation, mostly for people wanting to learn what simp
does.
Joachim Breitner (Mar 01 2024 at 16:05):
Yes, rw
or simp only
are also options. The tactic should probably check if it works and fall back to the proof term if it doesn’t. Another variant could be
calc
P (0 + 1 * n * 1)
_ = P (0 + n * 1) := by congr; exact (Nat.one_mul n)
_ = P (0 + n) := by congr; exact (Nat.mul_one n)
_ = P n := by congr; exact (Nat.zero_add n));
(rw
might fail once I do stuff underneath binders; not supported yet, but might come. Maybe simp_rw only
is better.)
Andrés Goens (Mar 01 2024 at 16:07):
Joachim Breitner said:
Hmm, not sure. One good thing about
calc
proofs is that each step has its own proof, so that they break individually when you refactor.If the goal is just to explain the simp, but keeping the simp, then something inspired like from
says
might work:example (n : Nat) : 0 + n = 0 + (n * 1) := by rw [Nat.mul_one, Nat.zero_add] performs 0 + n _ = n _ = 0 + n _ = 0 + n * 1
Or, turning it around, roughly your syntax. Ok, that might be useful indeed.
At this point, I wonder, is it useful to have this in the code, or would it suffice to be able to point at the proof and see it in the info view or so.
right, I think with rw
that doesn't work as easily, but if it's simp
then that would still be the case, i.e. if the same simp
set is used for every individual proof in calc
then in a refactor you would still have the individual step break the same way
Andrés Goens (Mar 01 2024 at 16:08):
i.e. thinking of the performs
as some sort of <;>
instead
Joachim Breitner (Mar 01 2024 at 16:40):
I put my prototype on https://github.com/nomeata/lean-calcify
Mario Carneiro (Mar 02 2024 at 06:05):
Joachim Breitner said:
hmm, no
convert_to
in Core :-)
Maybe we should fix that :wink: I think it has no prerequisites; the main one that was missing originally was congr
but this has since moved to core.
Kim Morrison (Mar 02 2024 at 11:42):
Yes, I agree convert
should be moved. It is super useful for everyone.
Kim Morrison (Mar 02 2024 at 11:43):
If someone would like to make the PR and ping me I can merge it. If you know how to do the @[builtin_tactic]
dance, great, if you don't, just note that you haven't done that in the PR description.
Joachim Breitner (Mar 12 2024 at 15:29):
PR for conv => calc
at https://github.com/leanprover/lean4/pull/3659
Kim Morrison (Mar 12 2024 at 23:39):
@Joachim Breitner looks good. CI seems to have got stuck. Can you push an empty commit or similar?
Joachim Breitner (Mar 13 2024 at 08:49):
I have seen CI getting stuck when I create a PR with gh pr create
and add a label at that time.
Last updated: May 02 2025 at 03:31 UTC