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:
refine
creates the?f_old : Nat -> Nat
metavariableintro
creates a new metavariable?m
with contextx : Nat ⊢ Nat
and 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?m
to?f_old
.revert
creates a metavariable?m : Nat -> Nat
, assigns?f_old := ?m x
, then renames?m
to?f_old
. (Note: I think there is a pretty printing oversight here: there are two metavariables named?f_old
now, sinceMetavarDecl
contains the name, so they both print as?f_old
, but only the newest?f_old
is what can be referred to, since that's what's in theMetavarContext.userNames
table. As such, you can see?f_old
in thehf
goal, but it's not thef_old
goal.)- Then
refine ?f_new
renames the newest?f_old
to?f_new
.
In the end, after instantiating metavariables, this is what we have:
- an
?hf
metavariable 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