Coq项目中的SProp:严格命题与证明无关性详解
引言
在Coq证明助手中,SProp(严格命题)是一个实验性功能,它引入了一种特殊的类型系统,用于处理证明无关性(proof irrelevance)的概念。本文将深入探讨SProp的核心概念、使用方法和注意事项。
什么是SProp?
SProp是Coq中的一种特殊类型分类(sort),代表"严格命题"(Strict Proposition)。它的核心特性是:
- 证明无关性:在SProp中的所有元素都被视为定义上相等的
- 不可累积性:不能将SProp类型强制转换为Type类型
- 特殊的消除规则:只能被消除到其他SProp类型中
启用与禁用SProp
SProp功能默认是启用的,但可以通过以下方式控制:
Set Allow StrictProp. (* 启用SProp *)
Unset Allow StrictProp. (* 禁用SProp *)
或者在启动时使用命令行参数-disallow-sprop
来禁用。
SProp的基本特性
证明无关性示例
Theorem irrelevance (A : SProp) (P : A -> Prop) :
forall x : A, P x -> forall y : A, P y.
Proof.
intros * Hx *.
exact Hx. (* 由于证明无关性,x和y被视为相同 *)
Qed.
不可累积性
Fail Check (fun (A:SProp) => A : Type). (* 这个检查会失败 *)
SProp与常规类型的交互
从SProp到Prop
我们可以使用包装类型将SProp值提升到Prop世界:
Inductive Box (A:SProp) : Prop := box : A -> Box A.
从Prop到SProp
使用"压缩"(squash)操作可以将常规类型转换为SProp:
Inductive Squash (A:Type) : SProp := squash : A -> Squash A.
SProp中的归纳类型
在SProp中定义的归纳类型大多数是"压缩"类型,只能被消除到其他SProp中。空类型是个例外:
Inductive sEmpty : SProp := . (* SProp中的空类型 *)
原始记录与SProp
在SProp中允许定义原始记录(primitive records),只要字段都是SProp类型:
Set Primitive Projections.
Record sProd (A B : SProp) : SProp := { sfst : A; ssnd : B }.
定义性UIP(唯一身份证明)
定义性UIP是一个特殊功能,允许在SProp中声明非压缩的归纳类型:
Set Definitional UIP.
Inductive seq {A} (a:A) : A -> SProp :=
srefl : seq a a.
这个功能引入了一个特殊的归约规则,使得当索引可转换时匹配会归约。
UIP的潜在问题
定义性UIP与非直谓性排序结合可能导致归约不终止:
Axiom all_eq : forall (P Q:Prop), P -> Q -> seq P Q.
Definition transport (P Q:Prop) (x:P) (y:Q) : Q
:= match all_eq P Q x y with srefl _ => x end.
(* 可能导致无限归约 *)
调试SProp问题
SProp的实现依赖于称为"相关性标记"(relevance marks)的内部机制。虽然通常不需要关注,但在某些情况下可能需要调试:
Set Printing Relevance Marks. (* 显示相关性标记 *)
注意事项
- SProp目前仍是实验性功能
- 通过字节码或本地代码编译的转换检查目前不支持证明无关性
- 错误的标记可能导致转换失败,此时会收到"Bad relevance"警告
结论
SProp为Coq提供了处理证明无关性的强大工具,特别适合需要严格区分计算内容和证明内容的场景。虽然目前仍是实验性功能,但它为形式化验证开辟了新的可能性。使用时应当注意其限制和潜在问题,特别是在启用定义性UIP时。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考