Zulip Chat Archive
Stream: lean4
Topic: Factoring out declarations with a macro
Son Ho (Jun 13 2025 at 10:37):
I'm trying to write a macro to factor out declarations with similar bodies. Here is a minimal example:
-- This command:
generic (x1, x2) def «%» := 0
/- Should generate:
def x1 := 0
def x2 := 0
-/
To support this minimal example, I managed to write the following custom elaboration (I simply explore the syntax tree and replace the string "%" whenever it appears in a name):
import Lean
open Lean Elab Command Term Meta
/-!
The trick is simple: we explore the syntax and replace `"%"` whenever it appears inside a name.
-/
def elabName (new : String) (n : Name) : Name :=
match n with
| .anonymous => .anonymous
| .str pre str =>
-- The special case: we replace occurrences of `%`
let str := if str == "%" then new else str
.str (elabName new pre) str
| .num pre i => (.num (elabName new pre) i)
def elabCommand (name : String) (stx : Syntax) : Syntax :=
match stx with
| .missing => .missing
| .node info kind args => .node info kind (args.map (elabCommand name))
| .atom info val => .atom info val
| .ident info rawVal val preresolved =>
.ident info rawVal (elabName name val) preresolved
def elabCommands (names : Array String) (cmd : TSyntax `command) : CommandElabM Unit := do
let elabOne (name : String) := do
let cmd := elabCommand name cmd
logInfo m!"cmd: {cmd}"
Lean.Elab.Command.elabCommand cmd
names.forM elabOne
syntax (name := genericCommand) "generic" "(" ident,* ")" command : command
@[command_elab genericCommand]
def genericCommandImpl : CommandElab := fun stx => do
match stx with
| `(genericCommand| generic ( $ids,* ) $stx) =>
let ids := ids.getElems.map fun id =>
match id.raw.getId with
| .str .anonymous id => id
| _ => ""
logInfo m!"ids: {ids}"
logInfo m!"stx: {stx}"
elabCommands ids stx
| _ => throwUnsupportedSyntax
This is a bit of a hack, but works with the caveat that when I define more than one theorem at once, the goto definition only works for the last declaration:
-- Definitions: ok
generic (x1, x2) def «%» := 0
#print x1 -- `def x1 : Nat := 0` - Can goto def
#print x2 -- `def x2 : Nat := 0` - Can goto def
-- One theorem: ok
generic (th1) theorem «%» : True := by simp
#check th1 -- Can goto def
-- More than one theorem: not ok
generic (th2, th3) theorem «%» : True := by simp
#check th2 -- **HERE**: can't goto def
#check th3 -- Can goto def
Does someone has an idea about what's going on? I dived into the code of elabDeclaration but I don't manage to figure what I do wrong and where the discrepancy comes from.
Last updated: Dec 20 2025 at 21:32 UTC