Zulip Chat Archive
Stream: lean4
Topic: existsi for already elaborated Expr
David Renshaw (Nov 09 2022 at 14:16):
In my work-in-progress on mk_iff
, I have the following function:
def existsi (mvar : MVarId) (e : Expr) : MetaM MVarId := do
let (subgoals,_) ← Elab.Term.TermElabM.run $ Elab.Tactic.run mvar do
Elab.Tactic.evalTactic (←`(tactic| refine ⟨?_,?_⟩))
let [sg1, sg2] := subgoals | throwError "expected two subgoals"
sg1.assign e
pure sg2
Is there a better way to do this? I kinda want to call the existing existsi
tactic, but that would involve delaborating e
from Expr
back to Syntax
, and it feels wrong.
(I've been in this situation a few times lately, where I'm working in MetaM
and everything is already elaborated, and I want to call some tactics that only defined to operate on syntax.)
Mario Carneiro (Nov 09 2022 at 14:17):
A trick you can do to interpolate exprs into syntax is to use assigned metavars. You create a new metavar, immediately assign it, and then use the ?a
syntax to refer to the metavar by name in the syntax
David Renshaw (Nov 09 2022 at 14:23):
Like this?
def existsi (mvar : MVarId) (e : Expr) : MetaM MVarId := do
let mvar' ← mkFreshExprMVar none
let mvarId' := mvar'.mvarId!
mvarId'.assign e
let (subgoals,_) ← Elab.Term.TermElabM.run $ Elab.Tactic.run mvar do
Elab.Tactic.evalTactic (←`(tactic| refine ⟨ ???, ?_⟩))
let [sg] := subgoals | throwError "expected one subgoal"
pure sg
David Renshaw (Nov 09 2022 at 14:23):
what goes in the ???
in the tactic syntax ?
Mario Carneiro (Nov 09 2022 at 14:24):
I think ?$(mkIdent mvarId'.name)
David Renshaw (Nov 09 2022 at 14:42):
Hm. I have
def existsi (mvar : MVarId) (e : Expr) : MetaM MVarId := do
let mvar' ← mkFreshExprMVar none (kind := MetavarKind.synthetic)
let mvarId' := mvar'.mvarId!
mvarId'.assign e
let (subgoals,_) ← Elab.Term.TermElabM.run $ Elab.Tactic.run mvar do
Elab.Tactic.evalTactic (←`(tactic| refine ⟨?$(mkIdent mvarId'.name):ident, ?_⟩))
let [sg] := subgoals | throwError "expected one subgoal"
pure sg
and it's giving me an error because it's still producing two subgoals, rather than just one.
Gabriel Ebner (Nov 09 2022 at 16:24):
It's usually easiest to create the synthetic metavatiable using elabTerm
, something like this should work:
withFreshMacroScope do
let mvar' ← elabTerm (←`(?a))
let mvarId' := mvar'.mvarId!
mvarId'.assign e
evalTactic (← `(tactic| refine ⟨?a, ?_⟩))
David Renshaw (Nov 09 2022 at 16:50):
yep, this seems to work
def existsi (mvar : MVarId) (e : Expr) : MetaM MVarId := do
let (subgoals,_) ← Elab.Term.TermElabM.run $ Elab.Tactic.run mvar do
withFreshMacroScope do
let mvar' ← Elab.Tactic.elabTerm (←`(?a)) none
let mvarId' := mvar'.mvarId!
mvarId'.assign e
Elab.Tactic.evalTactic (←`(tactic| refine ⟨?a,?_⟩))
let [sg] := subgoals | throwError "expected one subgoal"
pure sg
David Renshaw (Nov 09 2022 at 16:51):
Is the withFreshMacroScope
so that the mvar name ?a
does not collide with any existing mvars?
Scott Morrison (Nov 10 2022 at 05:55):
You could even factor that out as a MetaM level refine
, so that others don't have to jump through the same hoops. :-)
Thomas Murrills (Nov 10 2022 at 19:58):
This technique is really neat. It seems like a generic way to “use Expr
s directly in syntax” during elaboration, which I’ve found myself wanting to do a couple times even in my short time metaprogramming in Lean!
I wonder if it warrants being packaged into something that wraps around `()
somehow…like a special kind of “let”/“with”/“where”. It’d be neat to write something vaguely like
`(f ?a + ?b) with ?a := e, ?b := e'
where e e' : Expr
.
Mario Carneiro (Nov 10 2022 at 20:11):
well that e
would have to be Syntax
...
Mario Carneiro (Nov 10 2022 at 20:15):
I think a reasonable way to package this up would be a function like:
def toSyntax (e : Expr) : TermElabM Syntax := withFreshMacroScope do
let stx ← `(?a)
let mvar ← elabTerm stx none
mvar.mvarId!.assign e
pure stx
so you could just write
def existsi (mvar : MVarId) (e : Expr) : MetaM MVarId := do
let (subgoals, _) ← Elab.Term.TermElabM.run $ Elab.Tactic.run mvar do
Elab.Tactic.evalTactic (←`(tactic| refine ⟨$(← toSyntax e), ?_⟩))
let [sg] := subgoals | throwError "expected one subgoal"
pure sg
Thomas Murrills (Nov 10 2022 at 20:32):
well that
e
would have to beSyntax
...
I was imagining that that with
could be a macro that did what needed to be done with an Expr
e
behind the scenes to have this effect, but toSyntax
is nicer and keeps the types clean and visible! :)
Thomas Murrills (Nov 10 2022 at 20:34):
However, I am worried about the macro scopes…does toSyntax
automatically create the scopes necessary to avoid conflict with goals named a
?
David Renshaw (Nov 10 2022 at 20:36):
I think the withFreshMacroScope
in the first line of toSyntax
handles that.
Thomas Murrills (Nov 10 2022 at 20:36):
Oops—I’m on mobile and hadn’t realized that that code block scrolls right, lol!
Thomas Murrills (Nov 10 2022 at 20:38):
Anyway, would love to see this become part of Lean 4, as it’s super convenient. :)
Last updated: Dec 20 2023 at 11:08 UTC