Zulip Chat Archive
Stream: lean4
Topic: Eliminating intermediate metavariables
Eric Wieser (Apr 09 2025 at 16:12):
What can I do to replace f_old with f_new here?
import Lean
open Lean Meta Elab Tactic
theorem foo : ∃ f : Nat → Nat, f 1 = 1 := by
refine ⟨?f_old, ?hf⟩
intro x
revert x
refine ?f_new
/-- Goal state is
case f_new
⊢ Nat → Nat
case hf
⊢ (fun x ↦ ?f_old) 1 = 1
-/
Eric Wieser (Apr 09 2025 at 16:13):
(Note that this is hard to debug in the web editor since it doesn't show case names)
Aaron Liu (Apr 09 2025 at 16:14):
I think ?f_old is delay-assigned, so you just have to solve the goal.
Eric Wieser (Apr 09 2025 at 16:15):
Can it be un-delayed?
Aaron Liu (Apr 09 2025 at 16:17):
no idea
Eric Wieser (Apr 09 2025 at 17:14):
I've filed lean4#7883 for a related issue
Kyle Miller (Apr 09 2025 at 17:21):
Aaron is correct that ?f_old is involved in a delayed assignment, but ?f_old is not delayed assigned itself. Also, it's worth being very careful here, since the initial ?f_old is different from the ?f_old in the end. Here's the sequence:
refinecreates the?f_old : Nat -> Natmetavariableintrocreates a new metavariable?mwith contextx : Nat ⊢ Natand a new metavariable?m.37 : Nat -> Nat, then assigns?f_old := fun x => ?m.37 x, then records the delayed assignment?m.37 x := ?m, and then renames?mto?f_old.revertcreates a metavariable?m : Nat -> Nat, assigns?f_old := ?m x, then renames?mto?f_old. (Note: I think there is a pretty printing oversight here: there are two metavariables named?f_oldnow, sinceMetavarDeclcontains the name, so they both print as?f_old, but only the newest?f_oldis what can be referred to, since that's what's in theMetavarContext.userNamestable. As such, you can see?f_oldin thehfgoal, but it's not thef_oldgoal.)- Then
refine ?f_newrenames the newest?f_oldto?f_new.
In the end, after instantiating metavariables, this is what we have:
- an
?hfmetavariable whose type is(fun x => ?m.37 x) 1 = 1 - the delayed assignment
?m.37 x := ?f_new x
Delayed assignments don't instantiate until their value contains no metavariables.
In this case, we could safely instantiate the delayed assignment since the metavariable it depends on (?f_new) has a local context that's a sublist of the ?m.37 context. (Indeed, the local contexts are the same.) If that's something that's fast to check for, maybe instantiateMVars could be changed to go ahead and instantiate.
We could also write a metaprogram to force that kind of delayed assignment to instantiate.
Something that's also possible is writing a metaprogram to "push" delayed assignments, at the cost of creating new delayed assignments for every metavariable in the expression. It might be worth having it as a tactic, at least for debugging purposes.
Last updated: May 02 2025 at 03:31 UTC