Zulip Chat Archive
Stream: mathlib4
Topic: RFC: `lift` tactic postprocessing
Jakub Nowak (Jan 14 2026 at 17:45):
What do you think about adding customizable postprocessing step to lift tactic (via [lift_postprocess] attribute and/or custom procs)?
For example, by appropriately tagging Int.ofNat_add_ofNat, Int.cast_ofNat_Int, and Int.ofNat_inj we could have the following rewrites done automatically:
import Mathlib
example (a b : ℤ) : a + b = 42 := by
lift a to ℕ using sorry
lift b to ℕ using sorry
guard_target = Nat.cast a + Nat.cast b = (42 : ℤ)
-- This should be done automatically by `lift` tactic.
rw [Int.ofNat_add_ofNat a b]
rw [← Int.cast_ofNat_Int]
rw [Int.ofNat_inj]
guard_target = a + b = (42 : ℕ)
Jakub Nowak (Jan 14 2026 at 17:48):
Note that we can't really use simp for that, as usually the simp-normal form is with coercions pushed downwards, while for lift_postprocess we want to push coercions upwards with the hope of eventually eliminating them.
Ruben Van de Velde (Jan 14 2026 at 17:58):
Is this different to what norm_cast does?
Jakub Nowak (Jan 14 2026 at 18:10):
Oh, I didn't know about norm_cast. I think that's what I wanted.
Can we make this tactic more discoverable? By e.g. mentioning it in lift description or adding it to hint?
Jakub Nowak (Jan 14 2026 at 18:11):
Maybe we can make lift call norm_cast automatically by default?
Floris van Doorn (Jan 14 2026 at 22:11):
You might be interested in the tactic cheatsheet on the learn page of the community website
Floris van Doorn (Jan 14 2026 at 22:13):
I don't think that it is a good idea to make lift call norm_cast by default. But of course, nothing is preventing you from defining a macro my_lift that does lift and then calls norm_cast.
Eric Wieser (Jan 14 2026 at 22:13):
I'm not sure automatically combining tactics is a good idea, since then it just makes it harder to reason about what each tactic does
Snir Broshi (Jan 14 2026 at 22:42):
I think that's a strange view considering many existing tactics do this, e.g. rw = rewrite+rfl, obtain = have+rintro, norm_num calls simp only I think, etc
Do you feel the same way about these or do they differ in some sense from lift calling norm_cast?
Eric Wieser (Jan 14 2026 at 22:44):
I see your point about rw; though note in that case we also have the uncombined version still; similarly for norm_num and norm_num1. obtain can't be expanded using rintro + have as far as I known.
Kevin Buzzard (Jan 14 2026 at 22:45):
rintro := intro + rcases, obtain := have + rcases
Jireh Loreaux (Jan 14 2026 at 22:45):
The suggestion for lift + norm_cast is a bit different, because it's basically asking for lift to become lift <;> norm_cast at *, which would affect the entire context.
Snir Broshi (Jan 14 2026 at 22:45):
Oh sorry I meant rcases
Jireh Loreaux (Jan 14 2026 at 22:47):
That global effect is the biggest reason I wouldn't want it to be combined.
Jakub Nowak (Jan 15 2026 at 01:39):
Jireh Loreaux said:
That global effect is the biggest reason I wouldn't want it to be combined.
I was thinking about it too. Maybe we could implement a smarter version of norm_cast that goes upwards from the given starting subexpression/subexpressions (i.e. where the lift was applied) and only applies cast normalization on these (without going downwards into subexpressions)?
Jakub Nowak (Jan 15 2026 at 02:05):
I've made PR that adds norm_cast to documentation of lift: #33987
Yury G. Kudryashov (Jan 15 2026 at 02:55):
Does it make sense to add lift +normalize option?
Yury G. Kudryashov (Jan 15 2026 at 02:56):
I don't know; for me, lift; norm_cast at * is good enough.
Jakub Nowak (Jan 15 2026 at 02:59):
Yury G. Kudryashov said:
Does it make sense to add
lift +normalizeoption?
If it's just the same as lift ..; norm_cast at * then I guess not, and certainly it shouldn't be the default.
Last updated: Feb 28 2026 at 14:05 UTC