我在 Coq 的 SSReflect 扩展中发现了两个约定,它们似乎特别有用,但我还没有看到它们在较新的依赖类型语言(Lean、Agda、Idris)中得到广泛采用。
首先,可能的谓词被表示为布尔返回函数而不是归纳定义的数据类型。默认情况下,这带来了可判定性,为计算证明提供了更多机会,并通过避免证明引擎携带大量证明项来提高检查性能。我看到的主要缺点是在证明时需要使用反射引理来操作这些布尔谓词。
其次,具有不变量的数据类型被定义为包含简单数据类型加上不变量证明的依赖记录。例如,固定长度序列在 SSReflect 中定义如下:
Structure tuple_of : Type := Tuple {tval :> seq T; _ : size tval == n}.
A seq
以及该序列长度为某个值的证明。这与例如如何Idris 定义了这种类型:
data Vect : (len : Nat) -> (elem : Type) -> Type
一种依赖类型的数据结构,其中不变量是其类型的一部分。 SSReflect 方法的优点之一是它允许重用,因此例如为seq
关于它们的证明仍然可以使用tuple
(通过对底层进行操作seq
),而伊德里斯的方法函数如下reverse
, append
等等需要重写Vect
。 Lean 实际上在其标准库中具有等效的 SSReflect 风格,vector
,但它也有伊德里斯风格array
这似乎在运行时有一个优化的实现。
One SSReflect 导向书籍 http://ilyasergey.net/pnp/pnp.pdf甚至声称Vect n A
样式方法是一种反模式:
依赖类型语言(尤其是 Coq)中的一个常见反模式是将此类代数属性编码到数据类型和函数本身的定义中(一个典型的例子)
这种方法的主要特征是长度索引列表)。虽然这种方法看起来很吸引人,但正如它所证明的那样
依赖类型捕获数据类型及其函数的某些属性的能力,它
本质上是不可扩展的,因为总会有另一个感兴趣的属性,而该属性尚未被扩展
由数据类型/函数的设计者预见到,因此必须将其编码为外部事实
反正。这就是为什么我们提倡这种方法,其中数据类型和函数被定义为接近的
程序员尽可能定义它们的方式,以及它们的所有必要属性
分别证明。
因此,我的问题是,为什么这些方法没有被更广泛地采用。我是否遗漏了一些缺点,或者它们的优点在比 Coq 更好地支持依赖模式匹配的语言中可能不太重要?