Zulip Chat Archive

Stream: lean4

Topic: Most idiomatic way to (meta)programatically generate inducti


Alex Keizer (Apr 13 2022 at 14:05):

As part of a macro I'm implementing I want to generate an inductive type and add it to the environment.
I've found MonadEnv.addDecl that does the "adding to the environment" part, but it requires a fully elaborated Declaration object, so using it is quite painful.

So, I figured I could reuse the command elaborator if only I could produce a Syntax tree. However, the number of constructors, and the indices/parameters of my inductive type depend on the input to my macro, so generating this Syntax tree is not quite straightforward, either.

A third route seems to just generate a string with the inductive definition, using all the ease of string formatting/concatenation, and then feed that string to the lean parser somehow? In fact, I'm porting code from lean3 which does exactly this, using parser.with_input. Is there a lean4 equivalent to this method? I've tried to look for it, and Parser.runParserCategory seems to do it, but it's documented as "convenience function for testing", so I'm a bit hesitant to use it.

Is there a generally agreed upon clean/idiomatic way to generate new declarations in a macro's elaborator?
The last one seems easiest to use, but it feels quite hackish

Arthur Paulino (Apr 13 2022 at 14:14):

If you have the code as a String, you can call Lean's frontend like this. But I'm also interested in knowing the most appropriate way to do this.

But this is not clear to me: what type of data do you have that carries information about your inductive type in the first place? Do you have a "meta" inductive type that carries the data about your inductive type? A structure? A raw string?

Arthur Paulino (Apr 13 2022 at 14:17):

Expliciting the final use case with the code that you'd like to see working might help

Alex Keizer (Apr 13 2022 at 14:18):

I'm trying to implement a data macro as drop-in replacement for inductive, but then using QPFs as their foundation.
So I have an InductiveType struct that has the specification given by the user, and from that I want to derive the required QPF-constructions.

Alex Keizer (Apr 13 2022 at 14:19):

So, for example

data Tree (α β : Type 2) : Type _
  | nil : Tree α β
  | node (a : α) : (β  Tree α β)  Tree α β

Should turn into

inductive Tree.HeadT (β : Type 2) : Type _
  | nil : HeadT β
  | node : HeadT β

Simon Hudon (Apr 13 2022 at 14:20):

I think addDecl is the approach you need if you're creating an inductive type use terms that are already elaborated. There are helper functions like mkAppM that can help make it less painful by inferring some of the arguments for you. Going from Expr to Syntax and back to Expr gives you new ways for your compilation to fail, and you should be careful about going that way

Alex Keizer (Apr 13 2022 at 14:20):

Only the parameters that occur negatively are preserved, I want the same number of constructors, with the same name, but the type should just be a simple enumeration

Alex Keizer (Apr 13 2022 at 14:41):

The main difficulty is that I only have a fully elaborated InductiveType for the original data Tree declaration.
From that we can certainly make an InductiveType for Tree.HeadT, but it involves a few steps that feel redundant.

Nevertheless, you are right about the extra conversions being a bit brittle, so I'll try going that route a bit more

Alex Keizer (Apr 13 2022 at 14:45):

Arthur Paulino said:

If you have the code as a String, you can call Lean's frontend like this. But I'm also interested in knowing the most appropriate way to do this.

Thanks for the pointer, I'd somehow written runFrontend off because it takes a filename, and I didn't want to put my generated code in a file, I reckon the filename is only used for error reporting and I could provide a bogus name?
Otoh, it does run in IO, which makes me a bit suspicious again.

Sébastien Michelland (Apr 13 2022 at 14:45):

Have you seen elabInductiveViews? It takes a data structure as parameter, I think you could generate Tree.HeadT just from a list of strings.

Alex Keizer (Apr 13 2022 at 14:52):

Yeah, InductiveView takes a ref : Syntax, though. Do you reckon I could use a bogus value for ref?

Sébastien Michelland (Apr 13 2022 at 14:56):

Yes you can set Syntax.missing. Some other bits take Syntax as well, but you can just craft it. For instance, you could set

type? := some ( `((β: Type 2)  Type _)),

Assuming you are in a suitable monad such as CommandElabM.

Also I'm not sure this is idiomatic but I believe this is what the inductive command elaborates to.

Sébastien Michelland (Apr 13 2022 at 15:23):

I figure I could show you the code I'm using directly: https://github.com/opencompl/lean-mlir-semantics/blob/master/MLIRSemantics/Util/Metagen.lean#L110-L157
It's very much experimental and currently (1) sets dummy constructor arguments, and (2) hacks to use evalExpr, which is unsafe and thus normally not allowed in command elaborators. But it might help as far as generating an inductive is concerned.

The file itself is self-contained, you can run it without cloning the repo.


Last updated: Dec 20 2023 at 11:08 UTC