Each declaration has default visibility rules.
Generally speaking, all names are private by default, unless defined in a public section.
Even public names usually place the bodies of definitions in the private scope, and even proofs in exposed definitions are kept private.
The specific visibility rules for each declaration command are documented together with the declaration itself.
The private scope of a module may be imported into another module using the Lean.Parser.Module.importall modifier.
By default, this is only allowed if the imported module and the current module are from the same Lake package, as its main purpose is to allow for separating definitions and proofs into separate modules for internal organization of a library.
The Lake package or library option allowImportAll can be set to allow other packages to access to the current package's private scopes via Lean.Parser.Module.importimport all.
The imported private scope includes private imports of the imported module, including nested Lean.Parser.Module.importimport alls.
As a consequence, the set of private scopes accessible to the current module is the transitive closure of Lean.Parser.Module.importimport all declarations.
Definitions in Lean result in both a representation in the type theory that is designed for formal reasoning and a compiled representation that is designed for execution.
This compiled representation is used to generate machine code, but it can also be executed directly using an interpreter.
The code runs during elaboration, such as tactics or macros, is the compiled form of definitions.
If this compiled representation changes, then any code created by it may no longer be up to date, and it must be re-run.
Because the compiler performs non-trivial optimizations, changes to any definition in the transitive dependency chain of a function could in principle invalidate its compiled representation.
This means that metaprograms exported by modules induce a much stronger coupling than ordinary definitions.
Furthermore, metaprograms run during the construction of ordinary terms; thus, they must be fully defined and compiled before use.
After all, a function definition without a body cannot be run.
The time at which metaprograms are run is referred to as the metaprogramming phase, frequently just called the meta phase.
Just as they distinguish between public and private information, modules additionally distinguish code that is available in the meta phase from ordinary code.
Any declaration used as an entry point to compile-time execution has to be tagged with the Lean.Parser.Module.importmeta modifier, which indicates that the declaration is available for use as a metaprogram.
This is automatically done in built-in metaprogramming syntax such as Lean.Parser.Command.syntax : commandsyntax, Lean.Parser.Command.macro : commandmacro, and Lean.Parser.Command.elab : commandelab but may need to be done explicitly when manually applying metaprogramming attributes such as app_delab or when defining helper declarations.
A Parser.Command.declModifiersmeta definition may only access (and thus invoke) other Parser.Command.declModifiersmeta definitions in execution-relevant positions; a non-Parser.Command.declModifiersmeta definition likewise may only access other non-Parser.Command.declModifiersmeta definitions.
Meta Definitions
In this module, the helper function revArrays reverses the order of the elements in each array literal in a term.
This is called by the macro rev!.
Main.leanmodule
open Lean
variable [Monad m] [MonadRef m] [MonadQuotation m]
partial def revArrays : Syntax → m Term
| `(#[$xs,*]) => `(#[$((xs : Array Term).reverse),*])
| other => do
match other with
| .node k i args =>
pure ⟨.node k i (← args.mapM revArrays)⟩
| _ => pure ⟨other⟩
Invalid `meta` definition `_aux___macroRules_termRev!__1`, `revArrays` not marked `meta`macro "rev!" e:term : term => do
revArrays e
The error message indicates that revArrays cannot be used from the macro because it is not defined in the module's metaprogramming phase:
Invalid `meta` definition `_aux___macroRules_termRev!__1`, `revArrays` not marked `meta`
Marking revArrays with the Lean.Parser.Command.declModifiers`declModifiers` is the collection of modifiers on a declaration:
* a doc comment `/-- ... -/`
* a list of attributes `@[attr1, attr2]`
* a visibility specifier, `private` or `public`
* `protected`
* `noncomputable`
* `unsafe`
* `partial` or `nonrec`
All modifiers are optional, and have to come in the listed order.
`nestedDeclModifiers` is the same as `declModifiers`, but attributes are printed
on the same line as the declaration. It is used for declarations nested inside other syntax,
such as inductive constructors, structure projections, and `let rec` / `where` definitions. meta modifier allows the macro definition to call it:
#[3, 2, 1]
Libraries that were not originally part of the meta phase can be brought into it by importing a module with Parser.Module.importmeta import.
When a module is imported at the meta phase, all of its definitions are made available at that phase, whether or not they were marked Parser.Command.declModifiersmeta.
There is no meta-meta phase.
In addition to making the imported module's public contents available at the meta phase, Parser.Module.importmeta import indicates that the current module should be rebuilt if the compiled representation of the imported module changes, ensuring that modified metaprograms are re-run.
If a definition should be usable in both phases, then it must be defined in a separate module and imported at both phases.
Cross-Phase Code Re-Use
In this module, the function toPalindrome is defined in the meta phase, which allows it to be used in a macro but not in an ordinary definition:
Phases.leanmodule
open Lean
variable [Monad m] [MonadRef m] [MonadQuotation m]
meta def toPalindrome (xs : Array α) : Array α := xs ++ xs.reverse
meta def palArrays : Syntax → m Term
| `(#[$xs,*]) => `(#[$(toPalindrome (xs : Array Term)),*])
| other => do
match other with
| .node k i args =>
pure ⟨.node k i (← args.mapM palArrays)⟩
| _ => pure ⟨other⟩
macro "pal!" e:term : term => do
palArrays e
#[1, 2, 3, 3, 2, 1] ++ [6, 7, 8] : Array Nat#check pal! (#[1, 2, 3] ++ [6, 7, 8])
public def Invalid definition `colors`, may not access declaration `toPalindrome` marked as `meta`colors := toPalindrome #["red", "green", "blue"]
Invalid definition `colors`, may not access declaration `toPalindrome` marked as `meta`
Moving toPalindrome to its own module, Phases.Pal, allows this module to be imported at both phases:
Phases/Pal.leanmodule
public def toPalindrome (xs : Array α) : Array α := xs ++ xs.reverse
Phases.leanmodule
meta import Phases.Pal
import Phases.Pal
open Lean
variable [Monad m] [MonadRef m] [MonadQuotation m]
meta def palArrays : Syntax → m Term
| `(#[$xs,*]) => `(#[$(toPalindrome (xs : Array Term)),*])
| other => do
match other with
| .node k i args =>
pure ⟨.node k i (← args.mapM palArrays)⟩
| _ => pure ⟨other⟩
local macro "pal!" e:term : term => do
palArrays e
#[1, 2, 3, 3, 2, 1] ++ [6, 7, 8] : Array Nat#check pal! (#[1, 2, 3] ++ [6, 7, 8])
public def colors := toPalindrome #["red", "green", "blue"]
If the macro pal! were public (that is, if it was not declared with the local modifier) then the Lean.Parser.Module.importmeta import of Phases.Pal would need to be declared Lean.Parser.Module.importpublic as well.
In addition, the import must be public if the imported definition may be executed at compile time outside the current module, i.e. if it is reachable from some public Parser.Command.declModifiersmeta definition in the current module.
Use Parser.Module.importpublic meta import.
If the declaration is already declared Parser.Command.declModifiersmeta, then Parser.Module.importpublic import is sufficient.
Unlike definitions, most metaprograms are public by default.
Thus, most Lean.Parser.Module.importmeta import are also Parser.Module.importpublic in practice.
The exception is when a definition is imported solely for use in local metaprograms, such as those declared with Parser.Command.syntaxlocal syntax, Parser.Command.macrolocal macro, or Parser.Command.elablocal elab.
For convenience, Lean.Parser.Command.declModifiers`declModifiers` is the collection of modifiers on a declaration:
* a doc comment `/-- ... -/`
* a list of attributes `@[attr1, attr2]`
* a visibility specifier, `private` or `public`
* `protected`
* `noncomputable`
* `unsafe`
* `partial` or `nonrec`
All modifiers are optional, and have to come in the listed order.
`nestedDeclModifiers` is the same as `declModifiers`, but attributes are printed
on the same line as the declaration. It is used for declarations nested inside other syntax,
such as inductive constructors, structure projections, and `let rec` / `where` definitions. meta also implies Lean.Parser.Command.declModifiers`declModifiers` is the collection of modifiers on a declaration:
* a doc comment `/-- ... -/`
* a list of attributes `@[attr1, attr2]`
* a visibility specifier, `private` or `public`
* `protected`
* `noncomputable`
* `unsafe`
* `partial` or `nonrec`
All modifiers are optional, and have to come in the listed order.
`nestedDeclModifiers` is the same as `declModifiers`, but attributes are printed
on the same line as the declaration. It is used for declarations nested inside other syntax,
such as inductive constructors, structure projections, and `let rec` / `where` definitions. partial.
This can be overridden by giving an explicit Lean.Parser.Command.declaration : commandtermination_by metric (such as one suggested by Lean.Parser.Command.declaration : commandtermination_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 keep the amount of Lean.Parser.Command.declModifiers`declModifiers` is the collection of modifiers on a declaration:
* a doc comment `/-- ... -/`
* a list of attributes `@[attr1, attr2]`
* a visibility specifier, `private` or `public`
* `protected`
* `noncomputable`
* `unsafe`
* `partial` or `nonrec`
All modifiers are optional, and have to come in the listed order.
`nestedDeclModifiers` is the same as `declModifiers`, but attributes are printed
on the same line as the declaration. It is used for declarations nested inside other syntax,
such as inductive constructors, structure projections, and `let rec` / `where` definitions. meta annotations as small as possible.
This avoids locking otherwise-reusable declarations into the meta phase and it helps the build system avoid more rebuilds.
Thus, when a metaprogram depends on other code that does not itself need to be marked Lean.Parser.Command.declModifiers`declModifiers` is the collection of modifiers on a declaration:
* a doc comment `/-- ... -/`
* a list of attributes `@[attr1, attr2]`
* a visibility specifier, `private` or `public`
* `protected`
* `noncomputable`
* `unsafe`
* `partial` or `nonrec`
All modifiers are optional, and have to come in the listed order.
`nestedDeclModifiers` is the same as `declModifiers`, but attributes are printed
on the same line as the declaration. It is used for declarations nested inside other syntax,
such as inductive constructors, structure projections, and `let rec` / `where` definitions. meta, this other code should be placed in a separate module and not marked Lean.Parser.Command.declModifiers`declModifiers` is the collection of modifiers on a declaration:
* a doc comment `/-- ... -/`
* a list of attributes `@[attr1, attr2]`
* a visibility specifier, `private` or `public`
* `protected`
* `noncomputable`
* `unsafe`
* `partial` or `nonrec`
All modifiers are optional, and have to come in the listed order.
`nestedDeclModifiers` is the same as `declModifiers`, but attributes are printed
on the same line as the declaration. It is used for declarations nested inside other syntax,
such as inductive constructors, structure projections, and `let rec` / `where` definitions. meta.
Only the final module that actually registers a metaprogram needs the helpers to be in the meta phase.
This module should use Lean.Parser.Module.importpublic meta import to import those helpers and then define its metaprograms using built-in syntax like Parser.Command.elabelab, using Lean.Parser.Command.declaration : commandmeta def, or using Lean.Parser.Command.section : commandA `section`/`end` pair delimits the scope of `variable`, `include`, `open`, `set_option`, and `local`
commands. Sections can be nested. `section <id>` provides a label to the section that has to appear
with the matching `end`. In either case, the `end` can be omitted, in which case the section is
closed at the end of the file.
meta section.