Zulip Chat Archive
Stream: metaprogramming / tactics
Topic: unify
Fabian Glöckle (May 10 2022 at 15:52):
Hi, I seem to be unable to find this in the meta API:
Given an expr e and an expr M potentially containing metavariables, what is the library function which succeeds iff e and M unify under a suitable metavariable assignment? It does not seem to be tactic.unify, which fails e.g. on list nat and list A, A being a metavariable of type Type.
Or is this not what metavariables are made for / the elaborator is accessed differently / ... ?
(I am still an absolute metaprogramming newbie)
Damiano Testa (May 10 2022 at 15:58):
I'm also a newbie, so I do not really know an answer to your question. However, the code below did what I wanted and "unified metavariables":
open tactic
/-- A `tactic bool` computing whether the user-input `e` unifies with `e'`. -/
meta def expr.unify_with (e e' : expr) : tactic bool :=
succeeds $ unify e e'
Damiano Testa (May 10 2022 at 15:59):
This allowed me to pass _ * _ for instance and it unified with 3 * 4 and filled in the _ with a term of type nat.
Damiano Testa (May 10 2022 at 15:59):
However, in my use-case, the metavariables were explicit _, not some expr whose type I did not know. If this even makes sense...
Fabian Glöckle (May 10 2022 at 16:01):
Is _ the same thing as an expr.mvar?
Henrik Böving (May 10 2022 at 16:02):
_ will put a meta variable at the spot it's used yes.
Fabian Glöckle (May 10 2022 at 16:04):
meta def mk_mvar' (pp_name : name) (type : expr) : tactic expr := do
uniq_name ← mk_fresh_name,
return $ mvar uniq_name pp_name type
meta def mwe : tactic unit :=
do
m ← mk_mvar' `M `(Type),
n ← to_expr ``(_),
trace (to_raw_fmt n),
trace (to_raw_fmt m),
unify n `(nat),
unify m `(nat)
Fabian Glöckle (May 10 2022 at 16:05):
With output
(mvar _mlocal._fresh.2495.965 _mlocal._fresh.2495.965 (const 2 []))
(mvar _fresh.2495.962 M (sort 1))
and error
unify tactic failed, failed to unify
M : Type
and
ℕ : Type
Fabian Glöckle (May 10 2022 at 16:08):
Why is the type of the _ metavariable (const 2 [])?
Damiano Testa (May 10 2022 at 16:10):
If I understand correctly, the first unify succeeds: i.e. this works:
meta def mwe : tactic unit :=
do
m ← mk_mvar' `M `(Type),
n ← to_expr ``(_) tt ff,
trace (to_raw_fmt n),
trace (to_raw_fmt m),
unify n `(nat)
Fabian Glöckle (May 10 2022 at 16:11):
Yes, the _ metavariable unifies, but my handcrafted one doesn't
The difference is the type of the expr.mvar
But I do not see why it should be (const 2 [])... ?!
Fabian Glöckle (May 10 2022 at 16:11):
@Jannis Limperg maybe? :)
Damiano Testa (May 10 2022 at 16:12):
Sorry, I am not sure that I can be of further/any help! I think that the _ is important and should be literally present, but I do not know. :shrug:
Fabian Glöckle (May 10 2022 at 16:13):
Damiano Testa said:
Sorry, I am not sure that I can be of further/any help! I think that the
_is important and should be literally present, but I do not know. :shrug:
Yes, thank you a lot for pointing toward the underscore. That will work and I hope the pros will tell me what is behind the mysterious 2 ;)
Jannis Limperg (May 10 2022 at 16:18):
Exprs representing mvars and hypotheses often contain nonsensical types like these. infer_type will give you the right type. :shrug:
Fabian Glöckle (May 10 2022 at 16:18):
"/- [WARNING] Do not trust the types for mvar and local_const,
they are sometimes dummy values. Use tactic.infer_type instead. -/" I see
Jannis Limperg (May 10 2022 at 16:19):
Yep yep. (Fixed in Lean 4 thankfully.)
Fabian Glöckle (May 10 2022 at 16:19):
So how would I write this instead?
meta def mk_mvar_pis : expr → tactic (list expr × expr)
| (pi n _ d b) := do
p ← mk_mvar' n d,
(ps, r) ← mk_mvar_pis (instantiate_var b p),
return ((p :: ps), r)
| e := return ([], e)
Fabian Glöckle (May 10 2022 at 16:19):
(taken from mk_local_pis)
Fabian Glöckle (May 10 2022 at 16:20):
I would like to retrieve the body of a (nested) pi, but with something unifiable instead of var 0 etc inside
Jannis Limperg (May 10 2022 at 16:23):
I wrote a comprehensive library for this sort of task a while ago. docs#tactic.open_pis_metas should be what you're looking for.
Fabian Glöckle (May 10 2022 at 16:26):
wow great, exactly what I need!
Fabian Glöckle (May 10 2022 at 16:27):
I didn't find it because I was looking for mvars, not metas...
Fabian Glöckle (May 10 2022 at 16:28):
and I am learning from your code that one should not create mvars by the expr.mvar constructor but by mk_meta_var, right?
Jannis Limperg (May 10 2022 at 16:31):
If I used it there, probably. ;) I don't recall the details.
Jannis Limperg (May 10 2022 at 16:33):
Ah yeah if you use the expr constructor directly, you create a malformed expression. Metavariables which are referred to in an expression must be registered in the environment and mk_meta_var does exactly that.
Henrik Böving (May 10 2022 at 16:53):
Just as a learning experience for myself^^ @Jannis Limperg
i put all of my (admittedly rather little) knowledge of the Lean 4 Meta API into this:
def foo : MetaM Unit := do
let u ← Meta.mkFreshLevelMVar
let a ← Meta.mkFreshExprMVar (mkSort u)
let listA := mkApp (mkConst `List [u]) a
let listNat ← Meta.mkAppM `List #[mkConst `Nat]
IO.println s!"{listA}"
IO.println s!"{listNat}"
if ←Meta.isDefEq listA listNat then
IO.println "wooo"
let newlistA ← Meta.instantiateMVars listA
IO.println s!"{newlistA}"
else
let newlistA ← Meta.instantiateMVars listA
IO.println s!"{newlistA}"
IO.println "booo"
#eval foo
but I get a boo and no meta variables are instantiated, why's that the case? The types it prints are
List.{?_uniq.1} ?_uniq.2
List.{0} Nat
those look to me like they should be solvable no?
Jannis Limperg (May 10 2022 at 18:45):
Took me a bit to figure out as well. It's a level issue: mkApp (mkConst `List [u]) a is not type-correct. List.{u} has type Type u -> Type u, but a has type Sort u. Complete fix:
import Lean
open Lean
open Lean.Meta
def foo : MetaM Unit := do
let u ← mkFreshLevelMVar
let a ← mkFreshExprMVar (mkSort (mkLevelSucc u))
let listA := mkApp (mkConst ``List [levelZero]) a
let listNat ← Meta.mkAppM ``List #[mkConst ``Nat]
IO.println s!"{listA}"
IO.println s!"{listNat}"
if ← Meta.isDefEq listA listNat then
IO.println "wooo"
let newlistA ← Meta.instantiateMVars listA
IO.println s!"{newlistA}"
else
let newlistA ← Meta.instantiateMVars listA
IO.println s!"{newlistA}"
IO.println "booo"
#eval foo
Other comments which are hopefully helpful:
- Use
``Listinstead of`Listwhere possible. - I usually use
trace[debug]for printf-style debugging.
Last updated: May 02 2025 at 03:31 UTC