<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pl on</title><link>https://topology2333.github.io/blog/categories/pl/</link><description>Recent content in Pl on</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 23 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://topology2333.github.io/blog/categories/pl/index.xml" rel="self" type="application/rss+xml"/><item><title>De Bruijn index</title><link>https://topology2333.github.io/blog/posts/pl/de-bruijn-index/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0000</pubDate><guid>https://topology2333.github.io/blog/posts/pl/de-bruijn-index/</guid><description>&lt;h2 id="目的">目的&lt;/h2>
&lt;p>考虑到 $\alpha$-等价性，$\lambda x. x$ 和 $\lambda y. y$ 在逻辑上是完全一样的，但写法不同，通过一套改写规则达到统一。&lt;/p>
&lt;hr>
&lt;h2 id="规则">规则&lt;/h2>
&lt;h3 id="定义">定义&lt;/h3>
&lt;p>改写后的符号用自然数标识，标识为从当前位置往外数，第几个 $\lambda$ 符号是我的绑定者。一些例子：&lt;/p>
&lt;ul>
&lt;li>$\lambda x. x$ 被改写成 $\lambda 1$&lt;/li>
&lt;li>$\lambda x. \lambda y. x$ 改写成 $\lambda \lambda 2$&lt;/li>
&lt;li>$\lambda x. \lambda y. \lambda z. x z (y z)$ 改写成 $\lambda \lambda \lambda 3 1 (2 1)$&lt;/li>
&lt;/ul>
&lt;p>形式化地，用 De Bruijn index 表示的 $\lambda$ 项，其语法为：&lt;/p>
$$M, N ::= n | M N | \lambda M$$
&lt;p>其中 $n$ 是大于 0 的自然数，表示变量；$M N$ 表示应用；$λ M$ 表示抽象。&lt;/p>
&lt;p>若变量 $n$ 处在至少 $n$ 个 $\lambda$ 的作用域内，则它是绑定变量；否则是自由变量。变量 $n$ 的绑定位置，是它所处作用域中从内向外数的第 $n$ 个 $\lambda$。&lt;/p>
&lt;hr>
&lt;h3 id="beta-规约">$\beta$ 规约&lt;/h3>
&lt;p>$(\lambda M) N$ 的 $\beta$-归约中，需要做三件事：&lt;/p>
&lt;ol>
&lt;li>找出 $M$ 中那些由最外层这个 $\lambda$ 绑定的变量；&lt;/li>
&lt;li>因为外层 $\lambda$ 被消去了，所以把 $M$ 中自由变量的编号整体减一；&lt;/li>
&lt;li>用参数 $N$ 替换对应位置，同时根据替换发生时所在的 $\lambda$ 层深，适当提升 $N$ 中自由变量的编号。&lt;/li>
&lt;/ol>
&lt;p>例子：$(\lambda\ \lambda\ 4\ 2\ (\lambda\ 1\ 3))\ (\lambda\ 5\ 1)$&lt;/p>
&lt;p>对应普通记号：$(\lambda x.\ \lambda y.\ z\ x\ (\lambda u.\ u\ x))\ (\lambda x.\ w\ x)$&lt;/p>
&lt;p>替换过程：&lt;/p>
&lt;ul>
&lt;li>标出将被替换的位置：$\lambda\ 4\ \Box\ (\lambda\ 1\ \Box)$&lt;/li>
&lt;li>外层 $\lambda$ 消失，自由变量编号整体减一：$\lambda\ 3\ \Box\ (\lambda\ 1\ \Box)$&lt;/li>
&lt;li>把方框 $\Box$ 替换为 $\lambda\ 5\ 1$，并根据所在 $\lambda$ 层提升自由变量编号：
&lt;ul>
&lt;li>第一个方框在 1 层 $\lambda$ 之下，因此替换成 $\lambda\ 6\ 1$&lt;/li>
&lt;li>第二个方框在 2 层 $\lambda$ 之下，因此替换成 $\lambda\ 7\ 1$&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>得到 $\lambda\ 3\ (\lambda\ 6\ 1)\ (\lambda\ 1\ (\lambda\ 7\ 1))$&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>替换的形式化定义：一个替换可以写成无限序列 $M_1.M_2.M_3\ldots$，其中第 $i$ 项 $M_i$ 表示第 $i$ 个自由变量将被替换成什么。所有相关变量编号加上 $k$ (shift) 记作 $\uparrow^k$。$\uparrow^0$ 是恒等替换。一个有限替换 $M_1.M_2.\ldots.M_n$ 实际是 $M_1.M_2.\ldots.M_n.(n+1).(n+2)\ldots$，也就是只替换前 $n$ 个变量，其余保持不变。&lt;/p>
&lt;p>替换作用记作 $M[s]$，替换的组合满足 $M[s_1\,s_2] = (M[s_1])[s_2]$。&lt;/p>
&lt;ul>
&lt;li>变量：第 $n$ 个变量在替换后变成第 $n$ 个替换项；&lt;/li>
&lt;li>应用：分别对左右两边替换；&lt;/li>
&lt;li>抽象：进入 $\lambda$ 以后，要把替换表整体向上调整一层
&lt;ul>
&lt;li>即 $(\lambda M)[s] = \lambda (M[1 . (s \uparrow^1)])$&lt;/li>
&lt;li>递归地，有 $(\lambda \lambda P)[s] = \lambda(\lambda P [1 . (s \uparrow^1)]) = \lambda (\lambda ( P [1 . 2 . (s \uparrow^2)] ))$&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>因此，$\beta$-归约可以简洁地写成 $(\lambda M)N \rightarrow M[N.1.2.3\ldots]$。例如，$(\lambda x. \lambda y. x)N$，也就是 $(\lambda \lambda 2)N$。$M = (\lambda 2)$，$s = N.1.2.3\dots$，要规约 $(\lambda 2)\ [N.1.2.3\dots]$。调整之后，$s' = 1 . (N[\uparrow^1]) . (1[\uparrow^1]) . (2[\uparrow^1]) \dots = 1 . (N\uparrow^1) . 2 . 3 . 4 \dots$。找到 $s'$ 的第二项，也就是 $N\uparrow^1$。所以，$(\lambda (\lambda 2))\ N$ 归约后的结果是 $\lambda (N\uparrow^1)$。&lt;/p>
&lt;p>对于 $\uparrow^1$ 的解释为：若 $(\lambda x. \lambda y. x)\ N$ 的 $N$ 中含有自由变量 $z$，那么它原来往上索引会超出最外层环境，而在 $\beta-$规约之后，最外层环境增加了一层，自由变量 shift 是非常合理的。&lt;/p>
&lt;hr>
&lt;h3 id="优缺点">优缺点&lt;/h3>
&lt;p>这样做的好处很明显：&lt;/p>
&lt;ul>
&lt;li>机器友好，判断两个表达式是否相等，只需要看数字序列是否一致，不需要进行复杂的变量更名（$\alpha$-conversion）。&lt;/li>
&lt;li>两个 $\alpha$-等价的项在内存中的表示是完全唯一的。可以直接通过哈希或简单的内存比较&lt;/li>
&lt;/ul>
&lt;p>但也有坏处：&lt;/p>
&lt;ul>
&lt;li>代换复杂：在进行 $\beta$-归约时，被代入项跨越的 $\lambda$ 层数发生变化，其内部所有指向外部的数字都必须进行加减校准。带来实现上的复杂度。&lt;/li>
&lt;li>人类难读。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="其他">其他&lt;/h2>
&lt;p>也有其他的策略，比如混合策略，对绑定变量使用 De Bruijn index 以方便计算，对自由变量使用名字以方便阅读。&lt;/p></description></item><item><title>Practical Foundations For Programming Languages</title><link>https://topology2333.github.io/blog/posts/pl/pfpl/</link><pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate><guid>https://topology2333.github.io/blog/posts/pl/pfpl/</guid><description>&lt;h2 id="简介">简介&lt;/h2>
&lt;p>编程语言是由 Judgments 和 Inference Rules 定义的。Judgments 例如 “$e$ 是一个表达式（$e \text{ exp}$）” 或者 “$\tau$ 是一个类型（$\tau \text{ type}$）”。Inference Rules 的标记方法是：横线上方写前提（Premises）横线下方写结论（Conclusion）：&lt;/p>
$$\frac{\Gamma \vdash e_1 : \text{nat} \quad \Gamma \vdash e_2 : \text{nat}}{\Gamma \vdash e_1 + e_2 : \text{nat}}$$
&lt;/br>
&lt;p>这种嵌套结构最后会成为证明树（Proof Tree）。&lt;/p>
&lt;p>此外还有 Concrete Syntax 和 Abstract Syntax 的区别。前者是字符串，比如 1 + 1 或者 +(1, 1)。后者是抽象结构，可以使用抽象绑定树 (Abstract Binding Trees, ABTs) 来表示。比如表达式 1 + 2 在抽象语法中可能被表示为 plus(num[1]; num[2])。这种消歧表示直接体现了语言的逻辑结构。&lt;/p>
&lt;hr>
$$\frac{\Gamma \vdash e_1 : \text{bool} \quad \Gamma \vdash e_2 : \tau \quad \Gamma \vdash e_3 : \tau}{\Gamma \vdash \text{if } e_1 \text{ then } e_2 \text{ else } e_3 : \tau}$$
&lt;p>中间的 $e_1$ 必须是 bool 类型；两个分支 $e_2$ 和 $e_3$ 必须具有相同的类型 $\tau$。结果类型就是那个共同的类型 $\tau$。&lt;/p></description></item></channel></rss>