Lean 中的定义会产生 类型论 中专为形式推理而设计的表示形式以及专为执行而设计的编译表示形式。
这种编译表示用于生成机器代码,但也可以使用解释器直接执行。
在 精化 期间运行的代码(例如 策略 或 macros)是定义的编译形式。
如果此编译表示发生更改,则由它创建的任何代码可能不再是最新的,并且必须重新运行。
由于编译器执行重要的优化,因此对函数的传递依赖链中的任何定义进行更改原则上可能会使其编译表示无效。
这意味着模块导出的元程序比普通定义产生更强的耦合。
此外,元程序在普通术语的构造期间运行;因此,它们在使用前必须被完全定义和编译。
毕竟,没有函数体的函数定义无法运行。
元程序运行的时间称为 元编程阶段,通常简称为 元阶段。
正如它们区分公共信息和私有信息一样,模块还区分元阶段可用的代码和普通代码。
任何用作编译时执行入口点的声明都必须使用 Lean.Parser.Module.importmeta 修饰符进行标记,这表明该声明可用作元程序。
这是在内置元编程语法(例如 Lean.Parser.Command.syntax : commandsyntax、Lean.Parser.Command.macro : commandmacro 和 Lean.Parser.Command.elab : commandelab)中自动完成的,但在手动应用元编程属性(例如 app_delab)或定义帮助器声明时可能需要显式完成。
Parser.Command.declModifiersmeta 定义只能访问(并因此调用)执行相关位置中的其他 Parser.Command.declModifiersmeta 定义;非 Parser.Command.declModifiersmeta 定义同样只能访问其他非 Parser.Command.declModifiersmeta 定义。
Meta Definitions
在此模块中,辅助函数 revArrays 反转术语中每个数组文字中元素的顺序。
这由宏 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
该错误消息表明 revArrays 无法从宏中使用,因为它未在模块的 元编程阶段 中定义:
Invalid `meta` definition `_aux___macroRules_termRev!__1`, `revArrays` not marked `meta`
使用 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 修饰符标记 revArrays 允许宏定义调用它:
#[3, 2, 1]
最初不属于元阶段的库可以通过使用 Parser.Module.importmeta import 导入模块来引入。
当模块在元阶段导入时,其所有定义都在该阶段可用,无论它们是否标记为 Parser.Command.declModifiersmeta。
不存在元元阶段。
除了使导入模块的公共内容在元阶段可用之外,Parser.Module.importmeta import 还指示如果导入模块的编译表示发生更改,则应重建当前模块,以确保重新运行修改后的元程序。
如果一个定义应该在两个阶段都可用,那么它必须在单独的模块中定义并在两个阶段导入。
Cross-Phase Code Reuse
在此模块中,函数 toPalindrome 是在元阶段定义的,这允许它在宏中使用,但不能在普通定义中使用:
Phases.leanmodule
open Lean
variable [Monad m] [MonadRef m] [MonadQuotation m]
meta def toPalindrome (xs : Array α) : Array α := xs ++ xs.reverse
meta partial 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`
将 toPalindrome 移至其自己的模块 Phases.Pal 允许在两个阶段导入该模块:
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 partial 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"]
如果宏 pal! 是公共的(即,如果未使用 local 修饰符声明它),则 Phases.Pal 的 Lean.Parser.Module.importmeta import 也需要声明为 Lean.Parser.Module.importpublic。
此外,如果导入的定义可以在当前模块外部的编译时执行,即如果可以从当前模块中的某些公共 Parser.Command.declModifiersmeta 定义访问它,则导入必须是公共的。
使用 Parser.Module.importpublic meta import。
如果该声明已声明为 Parser.Command.declModifiersmeta,则 Parser.Module.importpublic import 就足够了。
与定义不同,大多数元程序默认是公共的。
因此,大多数 Lean.Parser.Module.importmeta import 实际上也是 Parser.Module.importpublic。
例外情况是导入定义仅用于本地元程序时,例如使用 Parser.Command.syntaxlocal syntax、Parser.Command.macrolocal macro 或 Parser.Command.elablocal elab 声明的定义。
作为指导原则,通常最好保持 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 注释的数量尽可能小。
这可以避免将可重用的声明锁定到 元阶段 中,并有助于构建系统避免更多的重建。
因此,当元程序依赖于本身不需要标记为 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 的其他代码时,该其他代码应放置在单独的模块中并且不标记为 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。
只有实际注册元程序的最终模块才需要帮助程序处于元阶段。
该模块应使用 Lean.Parser.Module.importpublic meta import 导入这些帮助程序,然后使用内置语法(如 Parser.Command.elabelab、使用 Lean.Parser.Command.declaration : commandmeta def 或使用 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)定义其元程序。