1 系统原理
1.1 编译过程
Lean 是一种函数式编程语言,其理论基础是归纳构造演算,因此也可将其作为交互式定理证明器
\[
\text{Lean String}
\xrightarrow{\text{Parser}}
\text{Lean Syntax}
\xrightarrow{\text{Elaboration}}
\text{Lean Expr}
\xrightarrow{\text{Evaluation}}
\text{IR}
\xrightarrow{\text{Compiler}}
\text{Binary Code}
\]
- 句法解析(Parsing):将 Lean
String元素转换为 LeanSyntax元素(具体语法树)- 先执行一次
syntax规则 - 然后执行零到多次
(macro; syntax)规则,直到不存在可匹配的宏
- 先执行一次
- 繁饰(Elaboration):将 Lean
Syntax元素转换为 LeanExpr元素(抽象语法树)、执行指令 - 求值(Evaluation):将
Expr元素转化为 LCNF(ANF 的一种)后,进一步转化为中间表示 - 编译(Compilation):通过 C 编译器或 LLVM 后端将中间表示转化为可执行代码
1.2 环境配置
1.2.1 终端工具
elan:管理 Lean 编译器工具链(类比于 Node.js 的nvm)show:列出所有工具链与 Lean 版本update [toolchain]:升级工具链与 Elan,未指定参数时升级所有工具链与 Elandefault <toolchain>:指定默认工具链toolchain <SUBCOMMAND>:修改或查询已安装工具链list:列出所有已安装工具链install:安装或升级指定工具链uninstall:卸载工具链link:通过符号链接到一个目录以创建自定义工具链
which <command>:列出运行指令的二进制文件run <toolchain> <command>:以指定工具链的环境配置运行指令self <SUBCOMMAND>:修改 Elanupdate:下载并安装 Elan 更新uninstall:卸载 Elan
lake:构建 Lean 包及其依赖项(类比于 Node.js 的npm)init <name> [template]:在当前目录创建 Lean 包new <name> [template]:在新目录创建一个 Lean 包build <targets>...:构建目标exe <exe> <args>:构建可执行文件并在lake环境内执行upload <tag>:上传构建 Artifact 到 GitHub Releaseclean:删除构建输出env <command>:在lake环境内执行指令script <SUBCOMMAND>:管理并运行工作区脚本list:列出可用脚本,可简写为lake scriptsrun <script>:运行脚本,可简写为lake run <script>doc <script>:打印指定脚本的文档字符串
update:升级依赖项并保存到 Manifest 文件serve:启动 Lean Language Server
lean:类型检查、编译各 Lean 文件以及提供有关当前正在编写的文件信息(类比于 Node.js 的node)--run:以余下参数调用文件中的main定义(类型必须为IO Unit)--o=oname/-o:创建olean文件--i=ilean/-i:创建ilean文件--c=fname/-c指定 C 输出文件名--bc=fname/-b指定 LLVM 二进制文件名--root=dir:设置根目录,从中计算输入文件的模块名
1.2.2 文件组织与模块
-
Lean 包是 Lake 代码分发的基本单位,包含如下内容
Main.lean:Lean 编译器在此文件查找主函数mainlean-toolchain:包含包所使用的 Lean 特定版本标识符-
lakefile.lean:lake构建配置,包含若干个目标(Lake 的基本构建单元),可用default_target属性标记默认目标package «package-name»:包声明,有且仅有一个lean_lib «target-name»:Lean 库声明extern_lib «target-name» (pkg):外部库声明,即从其他语言构建的静态库lean_exe «target-name» where:可执行文件target «target-name» (pkg) : α:自定义目标,即特定于具体执行平台的库或可执行文件的构建目标
构建得到的
.o、.c、.olean与.ilean文件均称作构建生成的 Facetpackage_facet «facet-name» (pkg : Package) : α:包 Facetlibrary_facet «facet-name» (lib : LeanLib) : α:库 Facetmodule_facet «facet-name» (mod : Module) : α:模块 Facet
-
库:共享同一配置的模块集合,通常包括
- 模块「根」:根目录下的 Lean 文件,用于决定库内应当包含的模块
- 一系列模块「团」:决定用于
lake build的模块
-
依赖项:也称作当前包的上游(当前包是依赖项的下游),通过如下语法引入
- 下次
lake build(或支持 Lean 的编辑器依赖更新)将自动下载对应依赖 - 依赖项具体版本信息会被记录于
lake-manifest.json以保证可再现性 - 对于
Mathlib,在执行lake build前应先执行lake exe cache get以规避从零开始构建
- 下次
工作区
Lake 的最大组织单元,它包括一个包、可移动依赖项与 Lake 环境
-
模块:Lake 构建系统的最小代码单元,通常由 Lean 源代码、一系列二进制库(
olean或ilean)以及系统共享库构成prelude:不导入Init.Prelude模块import:传递性地导入指定模块- Lean 将模块名的所有
.解释为目录,模块层级的最后一个标识符为文件 - 以尖括号
«...»包围模块名时可以使用空格或保留字
- Lean 将模块名的所有
模块名层级与命名空间层级
Lean 中的模块名层级与命名空间层级解耦
- 模块名层级是代码分发结构,命名空间层级是代码组织结构
import用于导入对应模块,即使得对应模块代码可用,此时可以使用open打开导入模块内的命名空间
1.3 风格与规约
- 文件结构:行宽不超过 \(100\) 字符
- 文件头:以
/- -/包围,包括版权信息、作者列表以及对文件内容的描述 - 导入:分行导入所需模块
- 模块文档字符串:以
/-! -/包围,使用 Markdown 语法,需要包括以下内容- 文件标题
- 文件内容的总结(包括主要定义与定理,证明技巧等)
- 文件中使用到的记号与参考文献(如有)
- 文件头:以
- 常量命名:以下规则对构类型与归纳类型的内部也适用
Prop、Type或Sort的元素(结构类型、归纳类型与类型类)使用UpperCamelCaseProp元素的实例使用snake_case,其余所有Types元素的实例使用lowerCamelCase- 函数命名方式与其最终返回值相一致,例如类型为
A → B → C的函数,命名方式与C的元素一致 - 部分缩略词被成组命名为全大写或全小写,具体以第一个字符的大小写为准,例如
LT或LE
- 变量命名:与通常的数学记号一致
- 宇宙层级:
u, v, w, ... - 泛型:
α, β, γ, ... - 泛型的元素:
x, y, z, ... - 命题:
a, b, c, ... - 假设:
h, h₁, ... - 谓词或关系:
p, q, r, ... - 列表或集合:
s, t, ... - 整数:
i, j, k, ... - 自然数:
m, n, k, ...
- 宇宙层级: