Zulip Chat Archive
Stream: lean4
Topic: Learning about metaprogramming
Adam Topaz (Jul 23 2022 at 23:37):
Hi all, I wanted to learn a little bit of metaprogramming in Lean4, so I am trying to write a version of mathlib's apply_fun
tactic.
So far, I have something fairly rudimentary with the following code (note: this is still very far from the full capability of mathlib's apply_fun
tactic):
import Lean
open Lean Elab.Tactic
syntax "applyFun " term " at " ident : tactic
elab_rules : tactic
| `(tactic|applyFun $f:term at $i:ident) =>
withMainContext do
let goal ← getMainGoal
match (← getLCtx).findFromUserName? i.getId with
| some d => do
evalTactic $ ← `(tactic|have $i := congrArg $f $i)
try replaceMainGoal [← Meta.clear (← getMainGoal) d.fvarId]
catch | _ => return
| none => Meta.throwTacticEx `applyFun goal f!"Hypothesis named {i} not found."
example (a b : Nat) (f : Nat → α) (h : a = b) : f a = f b := by
applyFun f at h
exact h
This seems to work (at least in the example) when we actually apply a function.
However, one thing I cannot figure out is how to make this work if f
has some type that has an appropriate CoeFun
instance (the mathlib Lean3 version is able to do this, and much more!). Can someone point me in the right direction here?
Also, I am very new to metaprogramming and programming in Lean4 in general, any advice about improving the code above would be much appreciated!
David Renshaw (Jul 23 2022 at 23:51):
Style-wise, the concrete syntax should remain apply_fun
. See https://github.com/leanprover-community/mathlib4/blob/dda51df299ce844cc686ecbd6f08c17a4a247078/Mathlib/Mathport/Syntax.lean#L426
Arthur Paulino (Jul 23 2022 at 23:55):
I think you want to do
try
evalTactic $ ← `(tactic|have $i := congrArg $f $i)
replaceMainGoal [← Meta.clear (← getMainGoal) d.fvarId]
catch _ => Meta.throwTacticEx `applyFun goal "tactic failed"
So even the have foo := congrArg ...
is guarded from errors
Arthur Paulino (Jul 23 2022 at 23:57):
I've never done instance inference in metaprogramming before, but I believe you could try mkAppM
with the inferInstance
function unless someone else tells us a more appropriate method
Arthur Paulino (Dec 07 2022 at 15:45):
How do I do this without writing syntax in my elaborator?
let natInt ← elabTerm (← `(Nat × Int)) none
Alexander Bentkamp (Dec 07 2022 at 15:52):
let natInt ← mkAppM ``Prod #[mkConst ``Nat, mkConst ``Int]
Arthur Paulino (Dec 07 2022 at 15:54):
Thanks! I had done
mkAppM ``Prod.mk ...
before, but I suspected I had done something wrong
Jannis Limperg (Dec 07 2022 at 16:33):
(FYI const ``Nat []
is now the preferred form and will save you from the mkConst
footgun of forgetting to specify the universe levels. Also, here you can use mkAppN
instead of mkAppM
.)
Arthur Paulino (Dec 07 2022 at 19:28):
Jannis Limperg said:
(FYI
const ``Nat []
is now the preferred form and will save you from themkConst
footgun of forgetting to specify the universe levels. Also, here you can usemkAppN
instead ofmkAppM
.)
Where is const
?
Arthur Paulino (Dec 07 2022 at 19:30):
Ah, Expr.const
probably
Scott Morrison (Dec 07 2022 at 22:34):
Regarding the mkConst
footgun, mathlib4 now defines (in Mathlib.Lean.Expr.Basic
)
/-- Same as `mkConst`, but with fresh level metavariables. -/
def mkConst' (constName : Name) : MetaM Expr := do
return mkConst constName (← (← getConstInfo constName).levelParams.mapM fun _ => mkFreshLevelMVar)
Better names and/or homes welcome if someone wants to move it upstream.
Gabriel Ebner (Dec 08 2022 at 05:42):
I believe the function should be called mkConstWithFreshMVarLevels
and should be put in Lean/Meta/Basic.lean
, roughly around here.
Last updated: Dec 20 2023 at 11:08 UTC