Zulip Chat Archive
Stream: general
Topic: macro for multiple commands
Jeremy Avigad (Sep 09 2023 at 20:55):
Is it possible to write a macro that expands to a sequence of Lean commands? For example #silly foo
expands to
def foo_1 := 1
def foo_2 := 2
def foo_3 := 3
and #silly bar
expands to
def bar_1 = 1
def bar_2 = 2
def bar_3 = 3
If macros are not the way to go, what is the easiest way to write such a command?
Alex J. Best (Sep 09 2023 at 21:05):
I couldn't find a way when I tried recently and instead came up with something only slightly more verbose in this thread
https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Type.20synonym.20boilerplate
Adam Topaz (Sep 09 2023 at 21:09):
You can just put them all into an quotation
Adam Topaz (Sep 09 2023 at 21:11):
macro "foo!!" : command =>
`(def foo1 := 1
def foo2 := 2
def foo3 := 3)
This works, but there is something that needs to be done with the actual names foo1
etc. that I forget at the moment.
Adam Topaz (Sep 09 2023 at 21:13):
Oh wait I misunderstood. But it's still possible, you just need to pass a nm:ident
as a parameter (for foo
resp. bar
)
Tomas Skrivan (Sep 09 2023 at 21:18):
Here is how you can chain it in a for loop.
elab "#empty" : command => pure ()
macro "#silly" id:ident : command => do
let mut command : TSyntax `command ← `(command| #empty)
for i in [0:3] do
let idn := mkIdent (id.getId.appendAfter s!"_{i}")
let n := Syntax.mkNumLit (toString i)
command ←
`($command
def $idn := $n)
pure command
#silly foo
#print foo_1
#silly bar
#print bar_2
The command #empty
is just a dummy command doing nothing and is used to initialize the value of command
. There has to be a better way to do this.
Adam Topaz (Sep 09 2023 at 21:19):
Aha! mkIdent
is what I was missing in my first message :)
Alex J. Best (Sep 09 2023 at 21:36):
And you don't really need to build them all as one syntax if you use an elab instead and just elab the defs one at a time in a loop instead of building one big syntax for the block
Kyle Miller (Sep 10 2023 at 13:35):
One pattern you find in things like the derive handlers is that functions return an Array (TSyntax `command)
, these are all concatenated, and then there's a main loop that elabs the combined results.
Kyle Miller (Sep 10 2023 at 13:38):
Another pattern is in the irreducible_def
module. It (locally) defines a syntax for a sequence of commands, where when it elaborates them it stops at the first error: https://github.com/leanprover-community/mathlib4/blob/7d308680dc444730e84a86c72357ad9a7aea9c4b/Mathlib/Tactic/IrreducibleDef.lean#L58
Mac Malone (Sep 10 2023 at 14:36):
The easiest way to expand/elaborate (an Array of) multiple commands at once is via mkNullNode
(which the command elaborator has special support for):
macro "#silly " id:ident : command =>
let cmds := -- [0:3].map ...
{raw := mkNullNode cmds}
(Sorry that I cannot write a proper mwe. I am on my phone away from the computer).
Jeremy Avigad (Sep 10 2023 at 14:46):
Thank you! All the information and examples are helpful.
Last updated: Dec 20 2023 at 11:08 UTC