“应用规则可先综合 $f:\Pi(x:A).B$,检查实参得到 $u:A$,再输出 $f,u:B[u/x]$。遇到隐式 Π 参数时,elaborator 生成 metavariable $?m…”
形式陈述 ​
依赖模式匹配让函数子句不仅拆解数据,还利用构造器结果精化索引。设归纳族
检查一个函数 nil 会产生约束 cons m a xs 会产生
一个完整检查器至少承担三项义务。coverage 要证明所有可居留输入都落入某个子句;impossible case 只有在索引约束与所有构造器结果冲突时才能省略;termination 或 productivity 检查要证明递归调用在结构或良基度量上下降。通过这些检查后,表面 clauses 可 elaboration 为带 motive 的 eliminator、case tree 或等价核心项;生成项
依赖匹配并非一条无条件的新核心规则。不同编译算法对 inaccessible/dot patterns、重叠子句和索引 unification 有不同完备范围;把它称为“消去子的语法糖”必须附带 elaboration 可靠性和所需相等原则。
直觉
普通模式匹配只回答“值是哪一个构造器”;依赖匹配还回答“既然是这个构造器,先前未知的索引现在必是什么”。构造器像一张带约束的收据:看到 nil,不仅知道没有元素,也知道长度必须是零。分支类型因此会随着匹配结果移动,某些表面组合则因索引矛盾根本没有居民。
coverage 的任务不是枚举语法上所有构造器笛卡尔积,而是枚举可达到的依赖状态。所谓 impossible clause 也不是程序员的一句愿望;检查器必须用构造器可辨识性、无混淆性或索引统一证明该状态为空。这样的证明若失败,省略分支就会把总函数伪装成部分函数。
例子与边界
非空向量的表头函数可写成
唯一实质子句是
head n (cons n a xs) = a
nil 的结果索引为 nil 分支可在
更能显示联合精化的是
首个向量匹配 nil 后 nil;首个为 cons 后,第二个也只能是同一后继长度的 cons,递归调用落到共同前驱。诸如 zip nil (cons b bs) 的交叉子句并非遗漏,而是索引冲突。这个例子不能机械改成两个普通列表:失去共享索引后,长度不等的分支重新变得可达,函数必须决定截断或报错。
另一个边界是相等证明匹配。若编译器允许对 refl 匹配,并在 index unification 中删除 with-K/without-K 一类理论选择,不能只由表面穷尽性决定。
推论与应用
依赖模式匹配使按长度、语法类型、协议状态索引的数据可用接近普通函数语言的方式编程。它可从构造器形状自动恢复等式约束,减少手写 transport;编译所得 case tree 还能供覆盖警告、不可达代码诊断和后端生成复用。可靠的 elaborator 最终仍提交小核心项,因此复杂匹配算法不必进入可信内核。
这种便利不会消除 motive 设计。返回类型若依赖被匹配值,编译器必须推断或由注解获得 motive;重叠模式还要证明选择顺序不改变声明式含义。加入 view pattern、guard 或一般递归后,coverage、可达性与终止性可能分别失效,应按独立检查报告,而不是用一次“pattern match accepted”概括全部保证。
参考资料
- Thierry Coquand, “Pattern Matching with Dependent Types,” Proceedings of the 1992 Workshop on Types for Proofs and Programs, 1992, pp. 71–83,依赖匹配与消去规则的早期系统化处理。
- Ulf Norell, Towards a Practical Programming Language Based on Dependent Type Theory, PhD thesis, Chalmers University of Technology, 2007,dependent pattern matching、with-abstraction 与 Agda elaboration。
- The Agda Team, Agda User Manual, “Coverage Checking” and “Without-K” sections,coverage、absurd patterns 与 K 限制,accessed 2026。