“同一充分性页的独立 $L {\mathrm{fix}}$ 扩充还补齐不动点分支:逐个语义近似使用箭头关系,再在有限域的稳定位置收尾。计数器表示独立性则在纯传值语言中逐项证明二元环境引理,包括…”
形式陈述
设一个抽象数据类型的接口依赖隐藏表示类型,写成
,二者都有存在类型
并证明接口的每个组成部分保持它。若接口含初始值、更新与观察操作,典型义务是
以及
函数参数、结果、积与其他接口类型都要按相应逻辑关系逐层提升。只比较某次操作的返回值不够;更新后的表示仍在
令
证明桥梁分三步:先选
直觉
接口像一面只开了若干窗口的墙。两个实现背后的房间布局可以完全不同,客户端却只能从窗口递交请求、接收结果。表示关系不是要求两边内部状态长得一样,而是给出“这两个不同状态表达同一个抽象状态”的对应方式。
每个接口操作都必须把这条对应关系接续下去。初始值把两边放到同一个起点,更新操作维护对应,观察操作给出相同外部答案;于是客户端无论怎样组合这些操作,都没有机会走到关系之外。
例子与边界
固定一个纯计数器接口和客户端语言
先把一般结论落实到一个能逐条证明的模型。接口为
三个分量依次命名为 zero、read、inc;三元组是嵌套积的记法。计数器状态是不可变值,inc 返回一个新状态,客户端可以保留旧状态。本例不观察地址、时间或分配,也不含可变引用、异常、一般 fix 或按隐藏类型分支的操作。
客户端在最外层打开一次包,得到新鲜抽象类型
项包括变量、λ/应用、二元积/投影、let 是 λ 应用的语法糖。采用纯传值求值:先求函数与参数,再代入参数值;积的两个分量均求成值;条件式只求选中分支。客户端不能把
为使递归的终止与归纳义务明确,规定
先求
最终客户端返回 Nat,故
两个实现与三项关系义务
第一个包用自然数表示状态:
第二个包用有限单位列表表示状态:
列表构造及消去只用于私有实现。其观察函数由
选关系
现在逐项核验,而不是从相同签名推断行为相同。
- 初值满足
。 - 若
,两个read都返回 。 - 若
,则 ,所以两个inc返回的值仍在 中。
这些等式对全部自然数和有限单位列表成立,已经覆盖所有关系中的状态,而非只覆盖某张执行表。
从接口关系证明任意客户端
按客户端类型提升表示关系
令
Bool 取同一布尔值,Unit 只有
箭头分支要求
这里输入只需相关,通常并不具有相同具体类型或语法。定义按客户端类型表达式
开放项基本引理及全部构造
对所有
必须对整个环境作此陈述,才能在 λ 和递归绑定器下使用归纳假设。按类型推导归纳:
- 变量和常量。 变量用环境分量;零、Unit、布尔常量分别返回相关值;后继先由子项归纳得到相同自然数,再在两侧同时加一。
- 积和投影。 两个子项各自终止到相关分量,传值积构造返回相关积;投影从相关积取相同位置,得到对应分量。
- 抽象。 λ 已是值。任取相关参数值,将它们加入两套相关替换;函数体归纳假设给出相关且终止的结果,再由传值 β 求值规则得到箭头条件。
- 应用。 函数项与参数项的归纳假设分别提供相关函数值和相关参数值;把后一对交给箭头关系,得到两次调用的相关结果。求值前缀再接回完整应用。
- 条件式。 守卫归纳给出同一布尔值,故两边进入同一侧分支;该分支归纳给出结果。不要求两侧私有表示相等。
- 原始递归。 对
的归纳给出同一 。再对辅助求值的自然数 作归纳:零时用基础项 的类型推导归纳假设; 时,先由数值归纳得到相关累积值 ,再把相同前驱 和这对累积值加入环境,对步进项 使用类型推导归纳假设。它同时保证本轮终止和结果相关,即使 是 或箭头也成立。
这些是本页客户端文法的全部构造,let 随 λ/应用得到。有限列表的私有递归不属于客户端归纳;它的终止和 read 保持关系已经单独核验。
三项接口义务正好说明两个实现三元组在
这次从逻辑关系到观察的充分性是直接展开定义,无须借用传名、含发散语言中的另一份计算充分性定理。
高阶调用、分支和保留旧状态的账本
下面代码中的三元组展开是积投影的缩写,数字是零和后继的缩写。另定义
unpack [α, m] = M in
let (zero, read, inc) = m in
let saved : α = inc zero in
let iterate : Nat → (α → α) → α → α =
λk:Nat. λf:α→α. λx:α. recα(k; x; i,y. f y)
in
let chosen : α → α =
if iszero (read saved) then inc else λx:α. inc (inc x)
in
let later : α = iterate (succ (read saved)) chosen saved in
add (read later) (read saved)
read saved 为
| 阶段 | 自然数表示 | 单位列表表示 | read |
|---|---|---|---|
zero |
|||
saved |
|||
| 迭代一次 | |||
later |
|||
再读保留的 saved |
最终两边都返回
同一签名下仍能放入错误实现
令 inc 改为 unpack [α,m]=M in let (zero,read,inc)=m in read (inc zero) 却分别观察到正确包的
对照列表实现,从相关初值
其他接口与局部状态的边界
集合接口提供 empty、insert 与 member。实现一用保持有序且无重复的列表
,等号比较两边表示的数学集合。空列表与空树相关;向相关表示插入同一元素后,二者仍表示同一集合;对相关表示查询同一元素,member 返回相同布尔值。因此只使用这三个操作的客户端无法分辨收到的是列表包还是树包。
若接口新增 iterate 并承诺具体遍历顺序,集合相等已不足以保证结果相关:有序列表可能升序输出,树实现的遍历策略则可能不同。若接口暴露地址身份、内部节点、异常种类、终止行为或运行时间,这些也会进入可观察集合;要么加强
带局部可变状态的模块还会分配地址,并在后续调用中扩展堆关系。此时常用 Kripke possible worlds 记录两边位置的持续对应,用 step index 处理递归状态与高阶存储;朴素的静态
推论与应用
表示独立性支撑模块替换、数据结构重构和编译器表示优化。客户端证明只依赖抽象接口,维护者便可更换内部结构而不重做全部客户端证明;需要重证的是新旧实现满足同一关系义务。
存在类型负责隐藏 witness,表示独立性负责证明隐藏后的行为保证。两者缺一不可:没有类型封装,客户端可能直接检查表示;只有封装没有关系证明,一个错误实现也能满足表面签名,却不满足抽象行为。
参考资料
[1] Frank Pfenning,Representation Independence,15-814 Lecture16,2025-10-30,讲义,§1的存在关系、§§3–4的接口义务、§7的客户端替换。本文改用自然数/单位列表,限定一次外层unpack的总语言并独立展开客户端归纳。
[2] Frank Pfenning,Theorems for Free,15-814 Lecture15,2025-10-28,讲义,§3,pp.L15.3–4的值/计算、积、箭头及类型变量关系;§4的求值闭包。
[3] Robert Harper,Practical Foundations for Programming Languages,2nd ed.,2016,修订在线节选,§9.1,pp.73–74,任意结果类型的原始递归。本文明确采用严格累积值语义,区别于书中§9.2把尚未求值的递归调用直接代入步进体的规则。
- John C. Mitchell and Gordon D. Plotkin, “Abstract Types Have Existential Type,” ACM TOPLAS 10(3), 1988。
- John C. Reynolds, “Types, Abstraction and Parametric Polymorphism,” in Information Processing 83, 1983,relational account of data abstraction。
- Andrew M. Pitts and Ian D. B. Stark, “Operational Reasoning for Functions with Local State,” in Higher Order Operational Techniques in Semantics, Cambridge University Press, 1998。