Skip to content

Commit 17e6541

Browse files
committed
ch extra3
1 parent 49f3ab2 commit 17e6541

File tree

3 files changed

+61
-151
lines changed

3 files changed

+61
-151
lines changed

lean/extra/03_pretty-printing.lean

Lines changed: 48 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,28 @@
1-
/- # Extra: Pretty Printing
2-
The pretty printer is what Lean uses to present terms that have been
3-
elaborated to the user. This is done by converting the `Expr`s back into
4-
`Syntax` and then even higher level pretty printing datastructures. This means
5-
Lean does not actually recall the `Syntax` it used to create some `Expr`:
6-
there has to be code that tells it how to do that.
7-
In the big picture, the pretty printer consists of three parts run in the
8-
order they are listed in:
9-
10-
- the **[delaborator](https://github.com/leanprover/lean4/tree/master/src/Lean/PrettyPrinter/Delaborator)**
11-
this will be our main interest since we can easily extend it with our own code.
12-
Its job is to turn `Expr` back into `Syntax`.
13-
- the **[parenthesizer](https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Parenthesizer.lean)**
14-
responsible for adding parenthesis into the `Syntax` tree, where it thinks they would be useful
15-
- the **[formatter](https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Formatter.lean)**
16-
responsible for turning the parenthesized `Syntax` tree into a `Format` object that contains
17-
more pretty printing information like explicit whitespaces
18-
19-
## Delaboration
20-
As its name suggests, the delaborator is in a sense the opposite of the
21-
elaborator. The job of the delaborator is to take an `Expr` produced by
22-
the elaborator and turn it back into a `Syntax` which, if elaborated,
23-
should produce an `Expr` that behaves equally to the input one.
24-
25-
Delaborators have the type `Lean.PrettyPrinter.Delaborator.Delab`. This
26-
is an alias for `DelabM Syntax`, where `DelabM` is the delaboration monad.
27-
All of this machinery is defined [here](https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Delaborator/Basic.lean).
28-
`DelabM` provides us with quite a lot of options you can look up in the documentation
29-
(TODO: Docs link). We will merely highlight the most relevant parts here.
30-
31-
- It has a `MonadQuotation` instance which allows us to declare `Syntax` objects
32-
using the familiar quotation syntax.
33-
- It can run `MetaM` code.
34-
- It has a `MonadExcept` instance for throwing errors.
35-
- It can interact with `pp` options using functions like `whenPPOption`.
36-
- You can obtain the current subexpression using `SubExpr.getExpr`. There is
37-
also an entire API defined around this concept in the `SubExpr` module.
38-
39-
### Making our own
40-
Like so many things in metaprogramming the elaborator is based on an attribute,
41-
in this case the `delab` one. `delab` expects a `Name` as an argument,
42-
this name has to start with the name of an `Expr` constructor, most commonly
43-
`const` or `app`. This constructor name is then followed by the name of the
44-
constant we want to delaborate. For example, if we want to delaborate a function
45-
`foo` in a special way we would use `app.foo`. Let's see this in action:
1+
/- # 额外内容:美观打印
2+
Lean 的美观打印器用于向用户呈现已繁饰的术语。这是通过将 `Expr` 转换回 `Syntax`,然后再转换为更高级的美观打印数据结构来完成的。这意味着 Lean 实际上不会记住它用来创建某些 `Expr` 的 `Syntax`:必须有代码告诉它如何执行此操作。
3+
4+
从宏观角度来看,美观打印器由三个部分组成,按列出的顺序运行:
5+
6+
- **[反繁饰器](https://github.com/leanprover/lean4/tree/master/src/Lean/PrettyPrinter/Delaborator)**
7+
这是我们主要感兴趣的部分,因为我们可以轻松地用自己的代码扩展它。它的任务是将 `Expr` 转换回 `Syntax`。
8+
- **[括号添加器](https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Parenthesizer.lean)**
9+
负责在 `Syntax` 树中添加括号,它认为在某些地方括号会有帮助。
10+
- **[格式化器](https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Formatter.lean)**
11+
负责将加了括号的 `Syntax` 树转换为包含更多美观打印信息(如显式空格)的 `Format` 对象。
12+
13+
## 反繁饰
14+
顾名思义,反繁饰器在某种意义上是繁饰器的反面。反繁饰器的任务是将由繁饰器生成的 `Expr` 转换回 `Syntax`,如果再次繁饰,应该生成一个与输入行为相同的 `Expr`。
15+
16+
反繁饰器的类型是 `Lean.PrettyPrinter.Delaborator.Delab`。这是 `DelabM Syntax` 的别名,其中 `DelabM` 是反繁饰 monad。所有这些机制都定义在[这里](https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Delaborator/Basic.lean)。`DelabM` 为我们提供了很多选项,您可以在文档中查看(TODO:文档链接)。这里我们只强调最相关的部分。
17+
18+
- 它有一个 `MonadQuotation` 实例,允许我们使用熟悉的引用语法声明 `Syntax` 对象。
19+
- 它可以运行 `MetaM` 代码。
20+
- 它有一个 `MonadExcept` 实例用于抛出错误。
21+
- 它可以使用 `whenPPOption` 等函数与 `pp` 选项交互。
22+
- 您可以使用 `SubExpr.getExpr` 获取当前的子表达式。`SubExpr` 模块中还围绕这个概念定义了整个 API。
23+
24+
### 创建我们自己的反繁饰器
25+
像元编程中的许多事情一样,反繁饰器基于属性,在这种情况下是 `delab` 属性。`delab` 期望以 `Name` 作为参数,这个名称必须以 `Expr` 构造函数的名称开头,最常见的是 `const` 或 `app`。此构造函数名称后面跟着我们想要反繁饰的常量名称。例如,如果我们想以特殊方式反繁饰函数 `foo`,我们将使用 `app.foo`。让我们来看一下实际操作:
4626
-/
4727

4828
import Lean
@@ -56,12 +36,10 @@ def delabFoo : Delab := do
5636
`(1)
5737

5838
#check foo -- 1 : Nat → Nat
59-
#check foo 13 -- 1 : Nat, full applications are also pretty printed this way
39+
#check foo 13 -- 1 : Nat, 整个应用同样被这样打印出来了
6040

6141
/-!
62-
This is obviously not a good delaborator since reelaborating this `Syntax`
63-
will not yield the same `Expr`. Like with many other metaprogramming
64-
attributes we can also overload delaborators:
42+
这显然不是一个好的反繁饰器,因为重新繁饰此 `Syntax` 不会产生相同的 `Expr`。像许多其他元编程属性一样,我们也可以重载反繁饰器:
6543
-/
6644

6745
@[delab app.foo]
@@ -71,65 +49,48 @@ def delabfoo2 : Delab := do
7149
#check foo -- 2 : Nat → Nat
7250

7351
/-!
74-
The mechanism for figuring out which one to use is the same as well. The
75-
delaborators are tried in order, in reverse order of registering, until one
76-
does not throw an error, indicating that it "feels unresponsible for the `Expr`".
77-
In the case of delaborators, this is done using `failure`:
52+
确定使用哪个反繁饰器的机制也是相同的。反繁饰器按照注册的相反顺序依次尝试,直到其中一个没有抛出错误,表明它「不对这个 `Expr` 负责」。在反繁饰器的情况下,这是通过使用 `failure` 来完成的:
7853
-/
7954

8055
@[delab app.foo]
8156
def delabfoo3 : Delab := do
8257
failure
8358
`(3)
8459

85-
#check foo -- 2 : Nat → Nat, still 2 since 3 failed
60+
#check foo -- 2 : Nat → Nat, 还是 2 因为 3 失败了
8661

8762
/-!
88-
In order to write a proper delaborator for `foo`, we will have to use some
89-
slightly more advanced machinery though:
63+
为了为 `foo` 编写一个合适的反繁饰器,我们将不得不使用一些稍微高级一点的机制:
9064
-/
9165

9266
@[delab app.foo]
9367
def delabfooFinal : Delab := do
9468
let e ← getExpr
95-
guard $ e.isAppOfArity' `foo 1 -- only delab full applications this way
69+
guard $ e.isAppOfArity' `foo 1 -- 只能像这样反繁饰整个应用
9670
let fn := mkIdent `fooSpecial
9771
let arg ← withAppArg delab
9872
`($fn $arg)
9973

10074
#check foo 42 -- fooSpecial 42 : Nat
101-
#check foo -- 2 : Nat → Nat, still 2 since 3 failed
75+
#check foo -- 2 : Nat → Nat, 还是 2 因为 3 失败了
10276

10377
/-!
104-
Can you extend `delabFooFinal` to also account for non full applications?
105-
106-
## Unexpanders
107-
While delaborators are obviously quite powerful it is quite often not necessary
108-
to use them. If you look in the Lean compiler for `@[delab` or rather `@[builtin_delab`
109-
(a special version of the `delab` attribute for compiler use, we don't care about it),
110-
you will see there are quite few occurrences of it. This is because the majority
111-
of pretty printing is in fact done by so called unexpanders. Unlike delaborators
112-
they are of type `Lean.PrettyPrinter.Unexpander` which in turn is an alias for
113-
`Syntax → Lean.PrettyPrinter.UnexpandM Syntax`. As you can see, they are
114-
`Syntax` to `Syntax` translations, quite similar to macros, except that they
115-
are supposed to be the inverse of macros. The `UnexpandM` monad is quite a lot
116-
weaker than `DelabM` but it still has:
117-
118-
- `MonadQuotation` for syntax quotations
119-
- The ability to throw errors, although not very informative ones: `throw ()`
120-
is the only valid one
121-
122-
Unexpanders are always specific to applications of one constant. They are registered
123-
using the `app_unexpander` attribute, followed by the name of said constant. The unexpander
124-
is passed the entire application of the constant after the `Expr` has been delaborated,
125-
without implicit arguments. Let's see this in action:
78+
你能扩展 `delabFooFinal` 来处理非完整应用的情况吗?
79+
80+
## 反扩展器
81+
虽然反繁饰器非常强大,但很多情况下并不需要使用它们。如果你查看 Lean 编译器中的 `@[delab` 或 `@[builtin_delab`(编译器使用的 `delab` 属性的特殊版本,我们对此不关心),你会发现它们的出现次数很少。这是因为大多数美观打印实际上是由所谓的反扩展器完成的。与反繁饰器不同,反扩展器的类型是 `Lean.PrettyPrinter.Unexpander`,这实际上是 `Syntax → Lean.PrettyPrinter.UnexpandM Syntax` 的别名。正如你所看到的,它们是 `Syntax` 到 `Syntax` 的转换,类似于宏,区别在于它们应该是宏的逆向操作。`UnexpandM` monad 比 `DelabM` 弱得多,但它仍然具有:
82+
83+
- 支持语法引用的 `MonadQuotation`
84+
- 能够抛出错误,尽管错误信息并不十分详细:`throw ()` 是唯一有效的错误
85+
86+
反扩展器始终针对一个常量的应用进行。它们通过 `app_unexpander` 属性注册,后面跟着该常量的名称。反扩展器在 `Expr` 经过反繁饰后被传递整个常量应用,而不包含隐式参数。让我们看看这个过程如何操作:
12687
-/
12788

12889
def myid {α : Type} (x : α) := x
12990

13091
@[app_unexpander myid]
13192
def unexpMyId : Unexpander
132-
-- hygiene disabled so we can actually return `id` without macro scopes etc.
93+
-- 禁用语法卫生,这样我们实际上可以返回 `id`,而不涉及宏范围等。
13394
| `(myid $arg) => set_option hygiene false in `(id $arg)
13495
| `(myid) => pure $ mkIdent `id
13596
| _ => throw ()
@@ -138,15 +99,10 @@ def unexpMyId : Unexpander
13899
#check myid -- id : ?m.3870 → ?m.3870
139100

140101
/-!
141-
For a few nice examples of unexpanders you can take a look at
142-
[NotationExtra](https://github.com/leanprover/lean4/blob/master/src/Init/NotationExtra.lean)
143-
144-
### Mini project
145-
As per usual, we will tackle a little mini project at the end of the chapter.
146-
This time we build our own unexpander for a mini programming language.
147-
Note that many ways to define syntax already have generation of the required
148-
pretty printer code built-in, e.g. `infix`, and `notation` (however not `macro_rules`).
149-
So, for easy syntax, you will never have to do this yourself.
102+
关于一些反扩展器的不错示例,你可以查看 [NotationExtra](https://github.com/leanprover/lean4/blob/master/src/Init/NotationExtra.lean)
103+
104+
### 项目示例
105+
像往常一样,我们将在本章末进行一个迷你项目。这次我们将为一个迷你编程语言构建自己的反扩展器。请注意,许多定义语法的方法已经内置了生成所需的漂亮打印代码,例如 `infix` 和 `notation`(但不包括 `macro_rules`)。因此,对于简单的语法,你不需要自己手动编写这些代码。
150106
-/
151107

152108
declare_syntax_cat lang
@@ -180,8 +136,7 @@ instance : Coe Ident (TSyntax `lang) where
180136
]
181137

182138
/-!
183-
As you can see, the pretty printing output right now is rather ugly to look at.
184-
We can do better with an unexpander:
139+
正如你所见,目前的漂亮打印输出相当难看。我们可以通过使用反扩展器来改进它:
185140
-/
186141

187142
@[app_unexpander LangExpr.numConst]
@@ -218,7 +173,5 @@ def unexpandLet : Unexpander
218173
]
219174

220175
/-!
221-
That's much better! As always, we encourage you to extend the language yourself
222-
with things like parenthesized expressions, more data values, quotations for
223-
`term` or whatever else comes to your mind.
176+
这样就好多了!一如既往,我们鼓励你自己扩展语言,比如添加带括号的表达式、更多的数据值、对 `term` 的引用,或其他你能想到的内容。
224177
-/

lean/main/04_metam.lean

Lines changed: 12 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ def myAssumption (mvarId : MVarId) : MetaM Bool := do
294294
295295
### 元变量深度
296296
297-
元变量深度也是一个较为小众的特性,但有时会派上用场。每个元变量都有一个*深度*(一个自然数),并且 `MetavarContext` 也有一个相应的深度。Lean 只有在元变量的深度等于当前 `MetavarContext` 的深度时才会为其赋值。新创建的元变量继承 `MetavarContext` 的深度,因此默认情况下,每个元变量都是可赋值的。
297+
元变量深度也是一个较为小众的特性,但有时会派上用场。每个元变量都有一个**深度**(一个自然数),并且 `MetavarContext` 也有一个相应的深度。Lean 只有在元变量的深度等于当前 `MetavarContext` 的深度时才会为其赋值。新创建的元变量继承 `MetavarContext` 的深度,因此默认情况下,每个元变量都是可赋值的。
298298
299299
这种机制在某些策略需要一些临时元变量,并且需要确保其他非临时元变量不会被赋值时会很有用。为了实现这一点,策略可以按照以下步骤进行:
300300
@@ -305,57 +305,21 @@ def myAssumption (mvarId : MVarId) : MetaM Bool := do
305305
306306
这种模式封装在 `Lean.Meta.withNewMCtxDepth` 中。
307307
308-
### Metavariable Depth
308+
## 计算
309309
310-
Metavariable depth is also a niche feature, but one that is occasionally useful.
311-
Any metavariable has a *depth* (a natural number), and a `MetavarContext` has a
312-
corresponding depth as well. Lean only assigns a metavariable if its depth is
313-
equal to the depth of the current `MetavarContext`. Newly created metavariables
314-
inherit the `MetavarContext`'s depth, so by default every metavariable is
315-
assignable.
310+
计算是依值类型理论的核心概念。项 `2`、`Nat.succ 1` 和 `1 + 1` 在计算出相同值的意义上是等价的。我们称它们为**定义等价**。从元编程的角度来看,问题在于定义等价的项可能由完全不同的表达式表示,但我们的用户通常期望适用于 `2` 的策略也适用于 `1 + 1`。因此,当我们编写策略时,必须做额外的工作,以确保定义等价的项得到类似的处理。
316311
317-
This setup can be used when a tactic needs some temporary metavariables and also
318-
needs to make sure that other, non-temporary metavariables will not be assigned.
319-
To ensure this, the tactic proceeds as follows:
312+
### 完全标准化
320313
321-
1. Save the current `MetavarContext`.
322-
2. Increase the depth of the `MetavarContext`.
323-
3. Perform whatever computation is necessary, possibly creating and assigning
324-
metavariables. Newly created metavariables are at the current depth of the
325-
`MetavarContext` and so can be assigned. Old metavariables are at a lower
326-
depth, so cannot be assigned.
327-
4. Restore the saved `MetavarContext`, thereby erasing all the temporary
328-
metavariables and resetting the `MetavarContext` depth.
314+
我们能对计算做的最简单的事情就是将项标准化。对于某些数字类型的例外情况,类型为 `T` 的项 `t` 的标准式是 `T` 构造函数的应用序列。例如,列表的标准式是 `List.cons` 和 `List.nil` 的应用序列。
329315
330-
This pattern is encapsulated in `Lean.Meta.withNewMCtxDepth`.
331-
332-
333-
## Computation
334-
335-
Computation is a core concept of dependent type theory. The terms `2`, `Nat.succ
336-
1` and `1 + 1` are all "the same" in the sense that they compute the same value.
337-
We call them *definitionally equal*. The problem with this, from a
338-
metaprogramming perspective, is that definitionally equal terms may be
339-
represented by entirely different expressions, but our users would usually
340-
expect that a tactic which works for `2` also works for `1 + 1`. So when we
341-
write our tactics, we must do additional work to ensure that definitionally
342-
equal terms are treated similarly.
343-
344-
### Full Normalisation
345-
346-
The simplest thing we can do with computation is to bring a term into normal
347-
form. With some exceptions for numeric types, the normal form of a term `t` of
348-
type `T` is a sequence of applications of `T`'s constructors. E.g. the normal
349-
form of a list is a sequence of applications of `List.cons` and `List.nil`.
350-
351-
The function that normalises a term (i.e. brings it into normal form) is
352-
`Lean.Meta.reduce` with type signature
316+
将项标准化(即将其带入标准式)的函数是 `Lean.Meta.reduce`,其类型签名为:
353317
354318
```lean
355319
reduce (e : Expr) (explicitOnly skipTypes skipProofs := true) : MetaM Expr
356320
```
357321
358-
We can use it like this:
322+
我们可以这样使用它:
359323
-/
360324

361325
def someNumber : Nat := (· + 2) $ 3
@@ -367,18 +331,11 @@ def someNumber : Nat := (· + 2) $ 3
367331
-- Lean.Expr.lit (Lean.Literal.natVal 5)
368332

369333
/-!
370-
Incidentally, this shows that the normal form of a term of type `Nat` is not
371-
always an application of the constructors of `Nat`; it can also be a literal.
372-
Also note that `#eval` can be used not only to evaluate a term, but also to
373-
execute a `MetaM` program.
374-
375-
The optional arguments of `reduce` allow us to skip certain parts of an
376-
expression. E.g. `reduce e (explicitOnly := true)` does not normalise any
377-
implicit arguments in the expression `e`. This yields better performance: since
378-
normal forms can be very big, it may be a good idea to skip parts of an
379-
expression that the user is not going to see anyway.
380-
381-
The `#reduce` command is essentially an application of `reduce`:
334+
顺便说一下,这表明类型为 `Nat` 的项的标准式并不总是 `Nat` 构造函数的应用,它也可以是一个字面量。此外,注意 `#eval` 不仅可以用于计算项,还可以用于执行 `MetaM` 程序。
335+
336+
`reduce` 的可选填参数允许我们跳过表达式的某些部分。例如,`reduce e (explicitOnly := true)` 不会归一化表达式 `e` 中的任何隐式参数。这可以带来更好的性能:由于标准式可能非常庞大,跳过用户无须看到的表达式部分可能是个好主意。
337+
338+
`#reduce` 命令本质上就是 `reduce` 的一个应用:
382339
-/
383340

384341
#reduce someNumber

lean/main/10_cheat-sheet.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ let goal_type ← goal.getType
5757
* 抛出错误信息:`Lean.Meta.throwTacticEx name mvar message_data`
5858
其中,`name : Lean.Name` 是策略名,`mvar` 包含错误信息
5959
60-
用法:`Lean.Meta.throwTacticEx `tac goal (m!"unable to find matching hypothesis of type ({goal_type})")`
60+
用法:`Lean.Meta.throwTacticEx` tac goal (m!"unable to find matching hypothesis of type ({goal_type})")`
6161
其中,`m!` 格式化用于构建 `MessageData`,以便更好地打印项。
6262
6363
TODO: Add?

0 commit comments

Comments
 (0)