Zulip Chat Archive
Stream: metaprogramming / tactics
Topic: Making a KeyedDeclsAttribute
Max Nowak ๐ (Jan 28 2026 at 21:12):
Can someone give me a "hello world" example of a custom KeyedDeclsAttribute? So far I have managed to piece together the following, but it still doesn't let me use the attribute.
import Lean
open Lean
syntax (name := greeterAttr) "greeter" : attr
abbrev Greeter := Syntax -> CoreM Bool
unsafe builtin_initialize greeters : KeyedDeclsAttribute Greeter <-
mkElabAttribute Greeter `builtin_greeter `greeter .anonymous ``Greeter "greeter" decl_name%
And then in another file:
@[greeter] -- unknown attribute `greeterAttr`
def hello : Greeter := ...
Aaron Liu (Jan 28 2026 at 21:24):
maybe don't use builtin initialize?
Max Nowak ๐ (Jan 28 2026 at 21:27):
Using initialize instead doesn't help unfortunately.
Aaron Liu (Jan 28 2026 at 21:28):
I'll try take a look at the code
Aaron Liu (Jan 28 2026 at 21:30):
wow I can't tell if this is supposed to be used
Aaron Liu (Jan 28 2026 at 21:31):
reading the docs really gives the impression it's only for builtin attributes
Robin Arnez (Jan 28 2026 at 21:41):
your `greeter might need to be a ``greeterAttr
Robin Arnez (Jan 28 2026 at 21:41):
and yes, definitely not builtin_initialize
Robin Arnez (Jan 28 2026 at 21:41):
that's for bootstrapping stuff
Patrick Massot (Jan 28 2026 at 21:43):
KeyedDeclsAttribute does not appear anywhere in Mathlib. This is a very bad omen.
Max Nowak ๐ (Jan 28 2026 at 21:55):
With ''greeterAttr I now get "Unexpected attribute argument" when using the attribute. At least that's a different error message, so, progress!
Max Nowak ๐ (Jan 28 2026 at 22:08):
How does the @[macro] attribute work? In the Lean compiler source, there is:
unsafe def mkMacroAttributeUnsafe (ref : Name) : IO (KeyedDeclsAttribute Macro) :=
mkElabAttribute Macro `builtin_macro `macro Name.anonymous `Lean.Macro "macro" ref
builtin_initialize macroAttribute : KeyedDeclsAttribute Macro โ mkMacroAttribute decl_name%
Which seems almost exactly like what I'm doing, and yet my version doesn't work :(.
Robin Arnez (Jan 28 2026 at 22:08):
Well you need to specify an argument. I'll give you a rough idea of how to use elab attributes:
import Lean
open Lean Elab
abbrev Greeter := Syntax โ CoreM Bool
declare_syntax_cat greeting
unsafe initialize greeters : KeyedDeclsAttribute Greeter <-
mkElabAttribute Greeter `builtin_greeter `greeter .anonymous ``Greeter "greeter" decl_name%
-- idk why core doesn't have this
instance : MonadMacroAdapter CoreM where
getNextMacroScope := return (โ getThe Core.State).nextMacroScope
setNextMacroScope next := modifyThe Core.State fun s => { s with nextMacroScope := next }
partial def runGreeterFor (stx : Syntax) : CoreM Bool := withRef stx do
match โ liftMacroM (expandMacroImpl? (โ getEnv) stx) with
| some (_decl, stxNew?) =>
let stxNew โ liftMacroM <| liftExcept stxNew?
runGreeterFor stxNew
| _ =>
let kind := stx.getKind
let candidates := greeters.getEntries (โ getEnv) kind
let state โ Core.saveState
for candidate in candidates do
try
return โ candidate.value stx
catch ex => match ex with
| .internal id _ =>
if id == unsupportedSyntaxExceptionId then
state.restore
else
throw ex
| _ => throw ex
return false
Other file:
syntax (name := hiThere) "hi!" : greeting
@[greeter hiThere]
public meta def greetHiThere : Greeter
| `(greeting| hi!) => do
Lean.logInfo "Hi, there!"
return true
| _ => Lean.Elab.throwUnsupportedSyntax
run_meta
runGreeterFor (โ `(greeting| hi!))
Robin Arnez (Jan 28 2026 at 22:09):
(code adjusted from the definition of elabTerm)
Max Nowak ๐ (Jan 28 2026 at 22:14):
Is that also using syntax (name := greeterAttr) "greeter" : attr ?
Robin Arnez (Jan 28 2026 at 22:21):
No, not at all
Robin Arnez (Jan 28 2026 at 22:21):
We don't define syntax for the attribute
Robin Arnez (Jan 28 2026 at 22:21):
I think if we did, it would be confused because it already expects a certain builtin attribute syntax
Robin Arnez (Jan 28 2026 at 22:23):
Now, you probably also want
Basically, code copied from Lean.Elab.ElabRules
so you can do
elab "hi!" : greeting => do
Lean.logInfo "Hi, there!"
return true
run_meta
runGreeterFor (โ `(greeting| hi!))
Max Nowak ๐ (Jan 28 2026 at 22:32):
Oh huh, I was not aware these attributes take an argument. But this actually solves a second issue I was about to have, so that's great!
Max Nowak ๐ (Jan 28 2026 at 22:34):
So, if I understand it correctly, for example macro_rules generates a @[macro theParser] def ... for every match arm, where theParser is the top level parser of the match arm?
Max Nowak ๐ (Jan 28 2026 at 22:35):
(or elab_rules, either way)
Max Nowak ๐ (Jan 28 2026 at 22:36):
All right I think I get it! This is cool! Thanks a lot for the help!
Last updated: Feb 28 2026 at 14:05 UTC