对称元编程的元理论
本笔记介绍了 原则性元编程 的简化变体,并概述了其健全性证明。该变体仅处理两个阶段之间的对话。程序可以包含引号,引号中可以包含拼接(拼接中可以包含引号,引号中可以包含拼接,等等)。或者程序可以从包含嵌入引号的拼接开始。本质上的限制是 (1) 一个术语可以包含顶层引号或顶层拼接,但不能同时包含两者,以及 (2) 引号不能直接出现在引号内,拼接不能直接出现在拼接内。换句话说,宇宙仅限于两个阶段。
在这个限制下,我们可以简化类型规则,使之始终只有两个环境,而不是拥有一个环境堆栈。这里介绍的变体与完整演算的不同之处还在于,我们用上下文类型规则替换了求值上下文。虽然这更冗长,但它使建立元理论变得更容易。
语法
Terms t ::= x variable
(x: T) => t lambda
t t application
’t quote
~t splice
Simple terms u ::= x | (x: T) => u | u u
Values v ::= (x: T) => t lambda
’u quoted value
Types T ::= A base type
T -> T function type
’T quoted type
操作语义
求值
((x: T) => t) v --> [x := v]t
t1 --> t2
---------------
t1 t --> t2 t
t1 --> t2
---------------
v t1 --> v t2
t1 ==> t2
-------------
’t1 --> ’t2
拼接
~’u ==> u
t1 ==> t2
-------------------------------
(x: T) => t1 ==> (x: T) => t2
t1 ==> t2
---------------
t1 t ==> t2 t
t1 ==> t2
---------------
u t1 ==> u t2
t1 --> t2
-------------
~t1 ==> ~t2
类型规则
类型判断的形式为 E1 * E2 |- t: T
,其中 E1, E2
是环境,*
是 ~
和 ’
之一。
x: T in E2
---------------
E1 * E2 |- x: T
E1 * E2, x: T1 |- t: T2
--------------------------------
E1 * E2 |- (x: T1) => t: T -> T2
E1 * E2 |- t1: T2 -> T E1 * E2 |- t2: T2
-------------------------------------------
E1 * E2 |- t1 t2: T
E2 ’ E1 |- t: T
-----------------
E1 ~ E2 |- ’t: ’T
E2 ~ E1 |- t: ’T
----------------
E1 ’ E2 |- ~t: T
(有趣的是,这看起来有点像圣诞树)。
健全性
元理论通常需要对两个判断进行相互归纳。
进展定理
- 如果
E1 ~ |- t: T
,则要么t = v
,其中v
是某个值,要么t --> t2
,其中t2
是某个项。 - 如果
’ E2 |- t: T
,则要么t = u
,其中u
是某个简单项,要么t ==> t2
,其中t2
是某个项。
通过对项进行结构归纳证明。
为了证明 (1)
- 变量、lambda 和应用的情况与 STLC 中的情况相同。
- 如果
t = ’t2
,则根据反演,我们有’ E1 |- t2: T2
,其中T2
是某个类型。根据第二个 归纳假设 (I.H.),我们有以下两种情况之一:t2 = u
,因此’t2
是一个值。t2 ==> t3
,因此’t2 --> ’t3
。
- 情况
t = ~t2
不可类型化。
为了证明 (2)
-
如果
t = x
,则t
是一个简单项。 -
如果
t = (x: T) => t2
,则要么t2
是一个简单项,在这种情况下t
也是一个简单项。要么根据第二个 I.H.t2 ==> t3
,在这种情况下t ==> (x: T) => t3
。 -
如果
t = t1 t2
,则以下三种情况之一适用:t1
和t2
是一个简单项,则t
也是一个简单项。t1
不是一个简单项。然后根据第二个 I.H.,t1 ==> t12
,因此t ==> t12 t2
。t1
是一个简单项,但t2
不是。然后根据第二个 I.H.t2 ==> t22
,因此t ==> t1 t22
。
-
情况
t = ’t2
不可类型化。 -
如果
t = ~t2
,则根据反演,我们有E2 ~ |- t2: ’T2
,其中T2
是某个类型。根据第一个 I.H.,我们有以下两种情况之一:t2 = v
。由于t2: ’T2
,我们必须有v = ’u
,其中u
是一个简单项,因此t = ~’u
。通过引号拼接归约,t ==> u
。t2 --> t3
。然后根据’t
的上下文规则,t ==> ’t3
。
替换引理
- 如果
E1 ~ E2 |- s: S
且E1 ~ E2, x: S |- t: T
,则E1 ~ E2 |- [x := s]t: T
。 - 如果
E1 ~ E2 |- s: S
且E2, x: S ’ E1 |- t: T
,则E2 ’ E1 |- [x := s]t: T
。
证明是通过对t
的类型推导进行归纳,类似于 STL 的证明(其中(2)比(1)更简单,因为我们不需要将 lambda 绑定与绑定变量x
交换)。连接两个假设的论据如下。
为了证明(1),令t = ’t1
。然后T = ’T1
,其中T1
是某种类型,最后一个类型规则是
E2, x: S ’ E1 |- t1: T1
-------------------------
E1 ~ E2, x: S |- ’t1: ’T1
根据第二个归纳假设,E2 ’ E1 |- [x := s]t1: T1
。根据类型,E1 ~ E2 |- ’[x := s]t1: ’T1
。由于[x := s]t = [x := s](’t1) = ’[x := s]t1
,我们得到[x := s]t: ’T1
。
为了证明(2),令t = ~t1
。然后最后一个类型规则是
E1 ~ E2, x: S |- t1: ’T
-----------------------
E2, x: S ’ E1 |- ~t1: T
根据第一个归纳假设,E1 ~ E2 |- [x := s]t1: ’T
。根据类型,E2 ’ E1 |- ~[x := s]t1: T
。由于[x := s]t = [x := s](~t1) = ~[x := s]t1
,我们得到[x := s]t: T
。
保持定理
- 如果
E1 ~ E2 |- t1: T
且t1 --> t2
,则E1 ~ E2 |- t2: T
。 - 如果
E1 ’ E2 |- t1: T
且t1 ==> t2
,则E1 ’ E2 |- t2: T
。
证明是通过对评估推导进行结构归纳。证明(1)类似于 STL 的证明,使用替换引理来处理 beta 归约情况,并添加了对引号项的归约,过程如下
- 假设最后一个规则是
t1 ==> t2 ------------- ’t1 --> ’t2
根据类型规则的反演,我们必须有
T = ’T1
,其中T1
是一个类型,使得t1: T1
。根据第二个归纳假设,t2: T1
,因此’t2:
T1`。
为了证明 (2)
-
假设最后一个规则是
~’u ==> u
。~’u
的类型证明必须具有以下形式E1 ’ E2 |- u: T ----------------- E1 ~ E2 |- ’u: ’T ----------------- E1 ’ E2 |- ~’u: T
因此,
E1 ’ E2 |- u: T
。 -
假设最后一个规则是
t1 ==> t2 ------------------------------- (x: S) => t1 ==> (x: T) => t2
根据类型反演,
E1 ' E2, x: S |- t1: T1
,其中T1
是一个类型,使得T = S -> T1
。根据归纳假设,t2: T1
。根据 lambda 表达式的类型规则,结果成立。 -
应用的上下文规则同样简单明了。
-
假设最后一个规则是
t1 ==> t2 ------------- ~t1 ==> ~t2
根据类型规则的反演,我们必须有
t1: ’T
。根据第一个归纳假设,t2: ’T
,因此~t2: T
。