Skip to content
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions Manual/ModuleSystem.lean
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,11 @@ When it comes to actual code execution, there is no point to a definition withou
Thus, in order to eagerly know what definitions _might_ be executed at compile time and so need to be available including their bodies (in some executable shape), any definition used as an entry point to compile-time execution has to be tagged with the new `meta` modifier.
This is automatically done in built-in metaprogramming syntax such as `syntax`, `macro`, and `elab` but may need to be done explicitly when manually applying metaprogramming attributes such as `@[app_delab]`.

A `meta` definition may access (and thus invoke) any `meta` or non-`meta` definition of the current module.
For accessing imported definitions, the definition must either have been marked as `meta` when it was declared or the import must be marked as such (`meta import` when the accessing definition is in the private scope and `public meta import` otherwise).

A `meta` definition may access (and thus invoke) other `meta` definitions only; a non-`meta` definition likewise may only access other non-`meta` definitions.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A `meta` definition may access (and thus invoke) other `meta` definitions only; a non-`meta` definition likewise may only access other non-`meta` definitions.
A `meta` definition may only access (and thus invoke) other `meta` definitions; a non-`meta` definition likewise may only access other non-`meta` definitions.

On a first read-through, the adverb came as a surprise

For imported definitions, the `meta` marker can be added after the fact using `meta import`.
`meta import`ing a definition already in the meta phase leaves it in that phase.
In addition, the import must be public if the imported definition may be compile-time executed outside the current module, i.e. if it is reachable from some public `meta` definition in the current module: use `public meta import` or, if already `meta`, `public import`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In addition, the import must be public if the imported definition may be compile-time executed outside the current module, i.e. if it is reachable from some public `meta` definition in the current module: use `public meta import` or, if already `meta`, `public import`.
In addition, the import must be public if the imported constant may be executed at compile time outside the current module.
In other words, if the imported `meta` constant is reachable from some public `meta` definition in the current module, then use `public meta import`.
If the constant is already `meta`, then `public import` is sufficient.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually prefer "declaration" over "constant" in user-facing text, is there some canonized stance on that?

This is usually the case, unless a definition was imported solely for use in `local` metaprograms.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is local annotated as code here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This is usually the case, unless a definition was imported solely for use in `local` metaprograms.
This is usually the case, unless a definition was imported solely for use in local metaprograms such as `local syntax/macro/elab/...`.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@david-christiansen Is this clearer?

```
module

Expand All @@ -141,6 +143,13 @@ local elab "my_elab" : command => do
...
```

For convenience, `meta` also implies `partial`.
This can be overriden by giving an explicit `termination_by` metric (such as one suggested by `termination_by?`), which may be necessary when the type of the definition is not known to be `Nonempty`.

As a guideline, it is usually preferable to apply `meta` as late as possible.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this text is a bit ambiguous: should it be applied late in the development process when Lean complains, or should it be done via import instead of an explicit modifier? It's the latter, but I think readers might be confused.

Thus helper definitions not directly registered as metaprograms do not need to be marked if they are located in a separate module.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Thus helper definitions not directly registered as metaprograms do not need to be marked if they are located in a separate module.
In particular, helper definitions that are not directly registered as metaprograms do not need to be marked if they are located in a separate module.

I think this is more of a "here's the important detail" sentence than an expression of causality/implication, right?

Comment on lines +149 to +150
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
As a guideline, it is usually preferable to apply `meta` as late as possible.
Thus helper definitions not directly registered as metaprograms do not need to be marked if they are located in a separate module.
As a guideline, it is usually preferable to keep the amount of `meta` annotations as small as possible in order to not lock reusable declarations into the meta phase unless necessary as well as to help the build system avoid more rebuilds.
Thus non-trivial amounts of code that does not contain registered metaprograms per se but that a registered metaprogram depends on should be located in a separate module and not marked `meta`.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the final module(s) actually registering metaprograms then should use `public meta import` to import those helpers and define its metaprograms using built-in syntax like `elab`, using `meta def`, or using `meta section`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Only the final module(s) actually registering metaprograms then should use `public meta import` to import those helpers and define its metaprograms using built-in syntax like `elab`, using `meta def`, or using `meta section`.
Only the final module that actually registers a metaprogram needs the helpers as `meta` definitions.
It should use `public meta import` to import those helpers and then define its metaprograms using built-in syntax like `elab`, using `meta def`, or using `meta section`.

I think two sentences makes this easier to parse.

Can we motivate this? I see two reasons: that it enables re-use and independent non-meta testing of these helpers, and that things are generally just easier when not in meta-mode. Is there a third reason?


# Common Errors and Patterns

The following list contains common errors one might encounter when using the module system and especially porting existing files to the module system.
Expand All @@ -164,6 +173,11 @@ The following list contains common errors one might encounter when using the mod
You might also see this as a kernel error when a tactic directly emits proof terms referencing specific declarations without going through the elaborator, such as for proof by reflection.
In this case, there is no readily available trace for debugging; consider using `@[expose] section`s generously on the closure of relevant modules.

: "failed to compile 'partial' definition" on `meta` definition

This can happen when a definition with a type that is not known to be `Nonempty` is marked `meta` or moved into a `meta section`, which both imply `partial` without a termination metric.
Use `termination_by?` to make the previously implicitly inferred termination metric explicit, or provide a `Nonempty` instance.

## Recipe for Porting Existing Files

Start by enabling the module system throughout all files with minimal breaking changes:
Expand Down
Loading