Zulip Chat Archive
Stream: lean4
Topic: Prevent the creation of new synthetic mvars
Leni Aniva (May 19 2024 at 00:43):
Suppose we have an expression like this
fun (p q: Prop) => (1: Nat)
and during the call to Meta.transform
on this function, the pre
phase converts (1: Nat)
to ?m.1 p q
, where ?m.1
is some delayed assigned metavariable dependent on some context.
When Meta.transform
finishes with the lambda, it converts ?m.1 p q
to another delayed assigned metavariable, ultimating resulting in
fun (p q: Prop) => ?m.3 p q p q
I suspect this is because ?m.1 p q
refers to p
and q
, so Lean creates a delayed assigned metavariable for it.
Is there a way to get fun (p q: Prop) => ?m.1 p q
instead?
Leni Aniva (May 19 2024 at 00:51):
The context of this problem is I want to merge chains of delayed assigned metavariables. For example if
?m.1 := ?m.2 [_f.10 := ...]
?m.2 := ?m.3 [_f.11 := ...]
then the result of the merger should be
?m.4 := ?m.3[_f.10 := ..., ?f.11 := ...]
Leni Aniva (May 19 2024 at 20:18):
I have a partial solution to this problem: Instead of creating delayed assigned metavariables, use (← Meta.mkLambdaFVars fvars inner).beta args
, where inner
is an expression that could contain metavariables in principle. This automatically handles the creation of metavariables.
The problem about this approach is sometimes mkLambdaFVars
creates additional mvars that are not assigned or delayed assigned. For example
Eq.{?_uniq.83} ?_uniq.87 ?_uniq.88 ?_uniq.89
after mkLambdaFVars
, I got
Eq.{?_uniq.83} (?_uniq.90 ?_uniq.68) (?_uniq.91 ?_uniq.68) (?_uniq.92 ?_uniq.68)
where ?_uniq.{90,91,92}
are not assigned or delayed assigned. This is very strange to me since it cuts off the connection between this expression and ?_uniq.{87,88,89}
.
It is very difficult to get a MWE from this since this behaviour vanishes for smaller examples
Leni Aniva (May 19 2024 at 21:29):
this check returns true, showing that the variables are unrelated
occursCheck { name = "_uniq.87".toName} (.mvar { name = "_uniq.90".toName })
as well as this one
occursCheck { name = "_uniq.87".toName} e
where e
is `Eq.{?_uniq.83} (?_uniq.90 ?_uniq.68) (?_uniq.91 ?_uniq.68) (?_uniq.92 ?_uniq.68)
.
so in this case mkLambdaFVars has decoupled a dependency of its input expression
Leni Aniva (May 19 2024 at 21:56):
This is the function I am using to force the instantiation of delayed mvars:
partial def instantiateDelayedMVars (eOrig: Expr) (level := 0): MetaM Expr := do
let padding := String.join $ List.replicate level "│ "
IO.println s!"{padding}Starting {toString eOrig}"
let mut result ← Meta.transform (← instantiateMVars eOrig)
(pre := fun e => e.withApp fun f args => do
let .mvar mvarId := f | return .continue
IO.println s!"{padding}├V {e}"
let mvarDecl ← mvarId.getDecl
let lctx ← MonadLCtx.getLCtx
if mvarDecl.lctx.any (λ decl => !lctx.contains decl.fvarId) then
let violations := mvarDecl.lctx.decls.foldl (λ acc decl? => match decl? with
| .some decl => if lctx.contains decl.fvarId then acc else acc ++ [decl.fvarId.name]
| .none => acc) []
panic! s!"Local context variable violation: {violations}"
if let .some assign ← getExprMVarAssignment? mvarId then
IO.println s!"{padding}├A ?{mvarId.name}"
assert! !(← mvarId.isDelayedAssigned)
return .visit (mkAppN assign args)
else if let some { fvars, mvarIdPending } ← getDelayedMVarAssignment? mvarId then
let substTableStr := String.intercalate ", " $ Array.zipWith fvars args (λ fvar assign => s!"{fvar.fvarId!.name} := {assign}") |>.toList
IO.println s!"{padding}├MD ?{mvarId.name} := ?{mvarIdPending.name} [{substTableStr}]"
if args.size < fvars.size then
throwError "Not enough arguments to instantiate a delay assigned mvar. This is due to bad implementations of a tactic: {args.size} < {fvars.size}. Expr: {toString e}; Origin: {toString eOrig}"
if !args.isEmpty then
IO.println s!"{padding}├── Arguments Begin"
let args ← args.mapM self
if !args.isEmpty then
IO.println s!"{padding}├── Arguments End"
if !(← mvarIdPending.isAssignedOrDelayedAssigned) then
IO.println s!"{padding}├T1"
let result := mkAppN f args
return .done result
let pending ← mvarIdPending.withContext do
let inner ← instantiateDelayedMVars (.mvar mvarIdPending) (level := level + 1)
IO.println s!"{padding}├Pre: {inner}"
let r ← Meta.instantiateLambda (← Meta.mkLambdaFVars fvars inner) args
pure r
-- Tail arguments
let result := mkAppN pending (List.drop fvars.size args.toList |>.toArray)
IO.println s!"{padding}├MD {result}"
return .done result
else
assert! !(← mvarId.isAssigned)
assert! !(← mvarId.isDelayedAssigned)
if !args.isEmpty then
IO.println s!"{padding}├── Arguments Begin"
let args ← args.mapM self
if !args.isEmpty then
IO.println s!"{padding}├── Arguments End"
IO.println s!"{padding}├M ?{mvarId.name}"
return .done (mkAppN f args))
IO.println s!"{padding}└Result {result}"
return result
where
self e := instantiateDelayedMVars e (level := level + 1)
Last updated: May 02 2025 at 03:31 UTC