Zulip Chat Archive

Stream: lean4

Topic: Force instantiate delay assigned metavariable


Leni Aniva (May 10 2024 at 00:17):

Is there a way to force all delay assigned metavariables to be instantiated?

For example, if I have an expression ?m.1 ?m.2, where

  • ?m.2 is not instantiated
  • ?m.1 is delay assigned to fun x => x + ?m.3
  • ?m.3 is not instantiated
    Is there a way to convert the expression ?m.1 ?m.2 to ?m.2 + ?m.3 or (fun x => x + ?m.3) ?m.2? i.e. I want all metavariables to be "as instantiated as possible".

Mario Carneiro (May 10 2024 at 03:58):

does instantiateMVars not do that?

Leni Aniva (May 10 2024 at 17:21):

Mario Carneiro said:

does instantiateMVars not do that?

No. It seems like it only instantiates delay assigned metavariables that don't refer to other metavariables

Jannis Limperg (May 10 2024 at 17:24):

That's right. The transformation you suggest, where delayed-assigned mvars are replaced with an abstraction, might also be sound, but afaik it's not implemented anywhere.

Joachim Breitner (May 10 2024 at 17:24):

I was under the impression that delayed meta variables are always just substitutions of other meta variables, not terms like x + ?m. Am I wrong?

Leni Aniva (May 10 2024 at 17:25):

Joachim Breitner said:

I was under the impression that delayed meta variables are always just substitutions of other meta variables, not terms like x + ?m. Am I wrong?

In this case its fun x => x + ?m

Joachim Breitner (May 10 2024 at 17:33):

Then my understanding of delayed meta variables is still wanting :-)

Leni Aniva (May 10 2024 at 17:39):

Jannis Limperg said:

That's right. The transformation you suggest, where delayed-assigned mvars are replaced with an abstraction, might also be sound, but afaik it's not implemented anywhere.

so delay assigned metavariables can only be instantiated if they are fully resolved?

Jannis Limperg (May 10 2024 at 17:48):

That's my understanding, yes.

Jannis Limperg (May 10 2024 at 17:48):

That's my understanding, yes.

Leni Aniva (May 10 2024 at 17:51):

Is there a way to determine what metavariables must be resolved before a delay assigned metavariable can be instantiated?

Leni Aniva (May 10 2024 at 17:52):

For example if ?m := fun x => x + ?m.3 is delay assigned, then ?m depends on ?m.3

Leni Aniva (May 10 2024 at 20:02):

Jannis Limperg said:

That's right. The transformation you suggest, where delayed-assigned mvars are replaced with an abstraction, might also be sound, but afaik it's not implemented anywhere.

Would it be possible to write this transformation using the expression transform functions?

Kyle Miller (May 10 2024 at 20:32):

Yeah, you can force delayed assignment metavariables to make progress if they're partially assigned. There's nothing built-in, but it's not so hard to write the transformation.

import Lean

open Lean Meta

/--
Instantiate metavariables, including partially assigned delayed assignment metavariables.
-/
def semiInstantiateMVars (e : Expr) : MetaM Expr := do
  Meta.transform ( instantiateMVars e)
    (pre := fun e => e.withApp fun f args => do
      if let .mvar mvarId := f then
        if let some decl  getDelayedMVarAssignment? mvarId then
          if args.size  decl.fvars.size then
            let pending  instantiateMVars (.mvar decl.mvarIdPending)
            if !pending.isMVar then
              return .visit <| ( mkLambdaFVars decl.fvars pending).beta args
      return .continue)

elab "check " t:term : tactic => Elab.Tactic.withMainContext do
  let mut e  Elab.Tactic.elabTerm t none
  if e.isFVar then
    if let some val  e.fvarId!.getValue? then
      e := val
  logInfo m!"{e}"

elab "check' " t:term : tactic => Elab.Tactic.withMainContext do
  let mut e  Elab.Tactic.elabTerm t none
  if e.isFVar then
    if let some val  e.fvarId!.getValue? then
      e := val
  let e'  semiInstantiateMVars e
  logInfo m!"{e'}"

example : True := by
  let h :  x y : Nat, 1 + x = 1 + y := fun x y => ?foo
  case' foo => refine congrArg _ ?_
  rotate_left
  check h   -- fun x y ↦ ?m.4924 x y
  check' h  -- fun x y ↦ congrArg (HAdd.hAdd 1) (?m.4955 x y)
  trivial

Kyle Miller (May 10 2024 at 20:33):

I've only tested this with this one example, so there might be bugs.

Leni Aniva (May 15 2024 at 04:51):

Kyle Miller said:

Yeah, you can force delayed assignment metavariables to make progress if they're partially assigned. There's nothing built-in, but it's not so hard to write the transformation.

import Lean

open Lean Meta

/--
Instantiate metavariables, including partially assigned delayed assignment metavariables.
-/
def semiInstantiateMVars (e : Expr) : MetaM Expr := do
  Meta.transform ( instantiateMVars e)
    (pre := fun e => e.withApp fun f args => do
      if let .mvar mvarId := f then
        if let some decl  getDelayedMVarAssignment? mvarId then
          if args.size  decl.fvars.size then
            let pending  instantiateMVars (.mvar decl.mvarIdPending)
            if !pending.isMVar then
              return .visit <| ( mkLambdaFVars decl.fvars pending).beta args
      return .continue)

elab "check " t:term : tactic => Elab.Tactic.withMainContext do
  let mut e  Elab.Tactic.elabTerm t none
  if e.isFVar then
    if let some val  e.fvarId!.getValue? then
      e := val
  logInfo m!"{e}"

elab "check' " t:term : tactic => Elab.Tactic.withMainContext do
  let mut e  Elab.Tactic.elabTerm t none
  if e.isFVar then
    if let some val  e.fvarId!.getValue? then
      e := val
  let e'  semiInstantiateMVars e
  logInfo m!"{e'}"

example : True := by
  let h :  x y : Nat, 1 + x = 1 + y := fun x y => ?foo
  case' foo => refine congrArg _ ?_
  rotate_left
  check h   -- fun x y ↦ ?m.4924 x y
  check' h  -- fun x y ↦ congrArg (HAdd.hAdd 1) (?m.4955 x y)
  trivial

I tried this but sometimes it gives

PANIC at Lean.LocalContext.get! Lean.LocalContext:227:14: unknown free variable
backtrace:
.lake/build/bin/test(lean_panic_fn+0xa3)[0x61d100e1d643]
.lake/build/bin/test(l_Lean_MetavarContext_MkBinding_mkBinding___lambda__2+0x171)[0x61d0ffee65a1]
.lake/build/bin/test(lean_apply_1+0x260)[0x61d100e270c0]
.lake/build/bin/test(l_EStateM_bind___rarg+0x16)[0x61d100d9db66]
.lake/build/bin/test(lean_apply_1+0x15d)[0x61d100e26fbd]
.lake/build/bin/test(lean_apply_2+0xd36)[0x61d100e287d6]
.lake/build/bin/test(l_Lean_MetavarContext_mkBinding+0x1a2)[0x61d0ffee8e12]
.lake/build/bin/test(l_Lean_Meta_mkLambdaFVars+0x3a8)[0x61d0fefe8098]
.lake/build/bin/test(l_Lean_Expr_withAppAux___at_Pantograph_instantiatePartialDelayedMVars___spec__1+0x629)[0x61d0fd87a349]
.lake/build/bin/test(l_Pantograph_instantiatePartialDelayedMVars___lambda__1+0xc9)[0x61d0fd87a8c9]
.lake/build/bin/test(lean_apply_5+0x363)[0x61d100e2a9c3]
.lake/build/bin/test(lean_apply_6+0xb65)[0x61d100e2be45]
.lake/build/bin/test(l_ReaderT_bind___at_Lean_Meta_zetaReduce___spec__14___rarg+0xb7)[0x61d0ffd9cc37]
.lake/build/bin/test(lean_apply_6+0x3a5)[0x61d100e2b685]
.lake/build/bin/test(l_Lean_Meta_withIncRecDepth___at_Lean_Meta_zetaReduce___spec__15+0x45b)[0x61d0ffd9d5eb]
.lake/build/bin/test(l_Lean_Meta_transform_visit___at_Lean_Meta_zetaReduce___spec__2+0x859)[0x61d0ffd926c9]
.lake/build/bin/test(l_Array_mapMUnsafe_map___at_Lean_Meta_zetaReduce___spec__10+0x1ec)[0x61d0ffd98fcc]
.lake/build/bin/test(l_Lean_Expr_withAppAux___at_Lean_Meta_zetaReduce___spec__13+0xa6f)[0x61d0ffd9b94f]
.lake/build/bin/test(l_Lean_Meta_transform_visit___at_Lean_Meta_zetaReduce___spec__2___lambda__1+0x7e1)[0x61d0ffd9ee11]
.lake/build/bin/test(lean_apply_7+0x4e1)[0x61d100e2c3f1]
.lake/build/bin/test(l_ReaderT_bind___at_Lean_Meta_zetaReduce___spec__14___rarg+0x264)[0x61d0ffd9cde4]
.lake/build/bin/test(lean_apply_6+0x3a5)[0x61d100e2b685]
.lake/build/bin/test(l_Lean_Meta_withIncRecDepth___at_Lean_Meta_zetaReduce___spec__15+0x45b)[0x61d0ffd9d5eb]
.lake/build/bin/test(l_Lean_Meta_transform_visit___at_Lean_Meta_zetaReduce___spec__2+0x859)[0x61d0ffd926c9]
.lake/build/bin/test(l_Lean_Meta_transform___at_Lean_Meta_zetaReduce___spec__1+0xf6)[0x61d0ffda0246]

Leni Aniva (May 15 2024 at 04:57):

Is it always true that the free variables in the mvarIdPending mvar of a delay assigned mvar are either in the fvars list or in the local context of the caller context? If there is only a simple substitution involved here maybe I could use a function that replace the occurrence of mvars instead of Expr.beta?

Leni Aniva (May 15 2024 at 17:52):

In this case I found an example where the lctx has _uniq.63, and the fvars list of a delay assigned mvar has _uniq.80, but the pending mvar is assigned to Eq.{?_uniq.83} ?_uniq.87 ?_uniq.88 ?_uniq.89


Last updated: May 02 2025 at 03:31 UTC