Zulip Chat Archive

Stream: metaprogramming / tactics

Topic: Learning metaprogramming


metakuntyyy (Jul 03 2025 at 20:17):

So yesterday I started learning metaprogramming and trying to print some information in the context. Today I wanted to see how I can create simple conclusions in a tactic.

import Lean
open Lean Elab Command Meta Tactic

syntax (name := c) "testTactic2"  : tactic

@[tactic c] def myTactic2 : Tactic := fun _ => withMainContext do
  let hyps := ( getLocalHyps).toList
  let c := hyps[0]!.fvarId!
  let type :=  c.getType
  logInfo m! "{type.ctorName}"
  let four := mkNatLit 4
  let d := Expr.instantiate1 type four
  logInfo m! "{d}"
  return

example (h:  x: Nat , x < 3) : False := by
  testTactic2
  sorry

I have played around a little bit. I want to instantiate x with four and I expected the logInfo to return 4<3 which I hoped I could add to the context. It, however prints h.

Robin Arnez (Jul 03 2025 at 20:24):

You're probably looking for instantiateForall

Robin Arnez (Jul 03 2025 at 20:25):

Or alternatively, you can use type.bindingBody!.instantiate1 four

metakuntyyy (Jul 03 2025 at 20:27):

Yes, it did work with instantiateForall, however I couldn't figure out by reading the docs what instantiate1 is supposed to do. It appears to do nothing.

Robin Arnez (Jul 03 2025 at 20:35):

instantiate1 instantiates loose bound variables. Remember, in ∀ x : Nat, x < 3 the body of the forall is represented as #0 < 3 with the bound variable #0 referring to the x (#0 = innermost bound variable, #1 = one outer, etc.). When you use instantiate1 on ∀ x : Nat, x < 3, it doesn't do anything because the only bound variable #0 is already bound by x. This is usually the case: Instead of having loose bound variables (.bvar n) we usually use free variables (.fvar x) which refer to variables from the local context. So instantiate1 usually doesn't do anything. But if you have loose bound variables, such as in the term #0 < 3, it will happily instantiate them (in particular the first one).

metakuntyyy (Jul 03 2025 at 20:36):

What would one such example be where instantiate1 does something, just so that I have a working reference

Robin Arnez (Jul 03 2025 at 20:38):

Again, you can take the binding body of the forall and then instantiate:

import Lean
open Lean Elab Command Meta Tactic

elab "testTactic2" : tactic => withMainContext do
  let hyps := ( getLocalHyps).toList
  let c := hyps[0]!.fvarId!
  let type :=  c.getType
  logInfo m! "{type.ctorName}"
  let four := mkNatLit 4
  let d := type.bindingBody!
  logInfo m! "{d}"
  let d := d.instantiate1 four
  logInfo m! "{d}"
  return

example (h:  x : Nat, x < 3) : False := by
  testTactic2
  sorry

As you can see, it's #0 < 3 => 4 < 3

metakuntyyy (Jul 03 2025 at 20:50):

Ok, now I am trying to add 4<3 to the list of hypotheses so that it can be displayed and added to the infoview

import Lean
open Lean Elab Command Meta Tactic

syntax (name := c) "testTactic2"  : tactic


@[tactic c] def myTactic2 : Tactic := fun _ => withMainContext do
  let hyps := ( getLocalHyps).toList
  let c := hyps[0]!.fvarId!
  let type :=  c.getType
  logInfo m! "{type.ctorName}"
  let four := mkNatLit 4
  let d  instantiateForall type [four].toArray
  logInfo m! "{d}"
  let fv  mkFreshFVarId
  let lctx getLCtx
  let ldecl := Lean.mkLocalDeclEx 44 fv Name.anonymous d .default
  have decl := lctx.addDecl ldecl

  return


example (h:  x : Nat, x < 3) : False := by
  testTactic2
  sorry

Seems not very idiomatic, addDecl is supposed to be a low-level API, I can't find a higher level api that lets me just add the local hypothesis.

Aaron Liu (Jul 03 2025 at 20:51):

I think the best way is to create a new goal withLocalDecl and assign the old one

metakuntyyy (Jul 03 2025 at 20:51):

Ah, I see. Let me try it out myself.

metakuntyyy (Jul 03 2025 at 21:01):

Are you sure that is what I want. This is the description of withLocalDecl

 Create a free variable `x` with name, binderInfo and type, add it to the context and run in `k`. Then revert the context.

Why would I want to revert the context, Also I don't understand how to pass the parameter to k.

import Lean
open Lean Elab Command Meta Tactic

syntax (name := c) "testTactic2"  : tactic


@[tactic c] def myTactic2 : Tactic := fun _ => withMainContext do
  let hyps := ( getLocalHyps).toList
  let c := hyps[0]!.fvarId!
  let type :=  c.getType
  logInfo m! "{type.ctorName}"
  let four := mkNatLit 4
  let d  instantiateForall type [four].toArray
  logInfo m! "{d}"
  let fv  mkFreshFVarId
  let x := withLocalDecl (String.toName "xy") .default d (k:=fun x => sorry)

  return

Aaron Liu (Jul 03 2025 at 21:01):

I think I did it!

What I did

metakuntyyy (Jul 03 2025 at 21:03):

Ah, nice. But I wanted to add it to the infoview and not replace the goal, I want that to the right it shows xy: 4 < 3

Aaron Liu (Jul 03 2025 at 21:06):

Like this?

import Lean
open Lean Elab Command Meta Tactic

syntax (name := c) "testTactic2"  : tactic


@[tactic c] def myTactic2 : Tactic := fun _ => withMainContext do
  let hyps := ( getLocalHyps).toList
  let c := hyps[0]!.fvarId!
  let type :=  c.getType
  logInfo m! "{type.ctorName}"
  let four := mkNatLit 4
  let d  instantiateForall type [four].toArray
  logInfo m! "{d}"
  let goal  getMainGoal
  let (mvarId, ass)  withLocalDecl `xy .default d fun e => do
    let mvar  mkFreshExprSyntheticOpaqueMVar ( goal.getType) ( goal.getTag)
    let ass  mkLambdaFVars #[e] mvar
    pure (mvar.mvarId!, Expr.app ass (.app (.fvar c) four))
  goal.assign ass
  replaceMainGoal [mvarId]

example (h:  x : Nat, x < 3) : False := by
  testTactic2
  sorry

Aaron Liu (Jul 03 2025 at 21:06):

Why do you want it to not replace the goal?

metakuntyyy (Jul 03 2025 at 21:07):

Well, I try to take baby steps. Also

  let (mvarId, ass)  withLocalDecl (String.toName "xy") .default d fun e => do

works as well which is nice.

metakuntyyy (Jul 03 2025 at 21:10):

Ah I see that this works, it adds it to the context. This seems very confusing to me as I expected it to just be able to push `xy d to the context. It seems to be a more convoluted operation.

metakuntyyy (Jul 03 2025 at 21:17):

The book uses this operation, it seems very useful on the first glance to me https://leanprover-community.github.io/mathlib4_docs/Lean/Elab/Tactic/Basic.html#Lean.Elab.Tactic.liftMetaTactic


Last updated: Dec 20 2025 at 21:32 UTC