Coq 为什么避风港';t新的依赖类型语言采用SSReflect';什么样的方法?

Coq 为什么避风港';t新的依赖类型语言采用SSReflect';什么样的方法?,coq,agda,idris,dependent-type,lean,Coq,Agda,Idris,Dependent Type,Lean,我在Coq的SSReflect扩展中发现了两种约定,它们似乎特别有用,但我还没有看到它们在更新的依赖类型语言(Lean、Agda、Idris)中被广泛采用 首先,可能的谓词表示为布尔返回函数,而不是归纳定义的数据类型。这在默认情况下带来了可判定性,为通过计算进行验证提供了更多机会,并通过避免验证引擎携带大型验证项来提高检查性能。我看到的主要缺点是在证明时需要使用反射引理来操作这些布尔谓词 其次,具有不变量的数据类型被定义为包含简单数据类型和不变量证明的依赖记录。例如,固定长度序列在SSRefle

我在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
定义的许多函数及其证明仍然可以与
元组一起使用(通过对底层
seq
进行操作),而Idris的方法函数如
reverse
append
等需要为
Vect
重写。实际上,Lean的标准库中有一个相当于SSReflect风格的库,
vector
,但它也有一个Idris风格的
数组
,它似乎在运行时有一个优化的实现

有人甚至声称
Vect n A
风格的方法是一种反模式:

在依赖类型语言(特别是Coq)中,一个常见的反模式是将这些代数属性编码到数据类型和函数本身的定义中(一个规范示例) 这种方法的一种是长度索引列表。虽然这种方法看起来很吸引人,但事实证明 依赖类型捕获数据类型的某些属性及其函数的能力 本质上是不可伸缩的,因为总会有另一个感兴趣的属性,而这是不可伸缩的 由数据类型/函数的设计者预见,因此必须将其编码为外部事实 无论如何这就是为什么我们提倡这种方法,在这种方法中,数据类型和函数被定义为紧密的 尽可能由程序员定义它们的方式,以及它们的所有必要属性 分别进行了证明


因此,我的问题是,为什么这些方法没有被更广泛地采用。是否有我遗漏的缺点,或者它们的优点在比Coq更好地支持依赖模式匹配的语言中没有那么重要?

我可以提供关于第一点的一些想法(将谓词定义为布尔返回函数)。我对这种方法最大的问题是:根据定义,函数不可能有bug,即使它的计算结果不是你想要的。在许多情况下,如果必须在谓词定义中包含谓词决策过程的实现细节,那么它也会模糊谓词的实际含义

在数学应用中,如果你想定义一个谓词,它是一般情况下不可判定的事物的特化,即使它恰好在你的特定情况下是可判定的,也会有问题。我在这里讨论的一个例子是用给定的表示来定义组:在Coq中,定义组的一种常见方法是集合类,其底层集合是生成器中的形式表达式,等式由“单词等价”给出。一般来说,这种关系是不可判定的,尽管在许多具体情况下是可判定的。然而,如果你被限制在用“问题”一词是可判定的表示来定义组,那么你就失去了定义将所有不同示例联系在一起的统一概念的能力,也就失去了对有限表示或有限表示组进行一般性证明的能力。另一方面,将单词等价关系定义为抽象的
Prop
或等价关系很简单(如果可能有点长)

就我个人而言,我倾向于首先给出尽可能透明的谓词定义,然后在可能的情况下提供决策过程(返回
{p}+{~p}
类型值的函数在这里是我的首选,尽管布尔返回函数也会很好地工作)。Coq的类型类机制可以提供一种方便的方式来注册此类决策程序;例如:

Class Decision (P : Prop) : Set :=
decide : {P} + {~P}.
Arguments decide P [Decision].

Instance True_dec : Decision True := left _ I.
Instance and_dec (P Q : Prop) `{Decision P} `{Decision Q} :
  Decision (P /\ Q) := ...

(* Recap standard library definition of Forall *)
Inductive Forall {A : Type} (P : A->Prop) : list A -> Prop :=
| Forall_nil : Forall P nil
| Forall_cons : forall h t, P h -> Forall P t -> Forall P (cons h t).
(* Or, if you prefer:
Fixpoint Forall {A : Type} (P : A->Prop) (l : list A) : Prop :=
match l with
| nil => True
| cons h t => P h /\ Forall P t
end. *)

Program Fixpoint Forall_dec {A : Type} (P : A->Prop)
  `{forall x:A, Decision (P x)} (l : list A) :
  Decision (Forall P l) :=
  match l with
  | nil => left _ _
  | cons h t => if decide (P h) then
                  if Forall_dec P t then
                    left _ _
                  else
                    right _ _
                else
                  right _ _
  end.
(* resolve obligations here *)
Existing Instance Forall_dec.
这在默认情况下带来了可判定性,为通过计算进行验证提供了更多机会,并通过避免验证引擎携带大型验证项来提高检查性能

您不必以“强制优化”的名义携带大量术语,如中所述。Agda确实有影响类型检查的强制(特别是宇宙如何计算是相关的),但我不确定仅在类型检查时使用的东西是否真的在运行时之前被擦除。无论如何,Agda有两个无关概念:
(eq:p≡ q) 
通常是不相关的(意思是
eq
在类型检查时是不相关的,因此它定义上等于此类类型的任何其他术语),并且
(x:A)
是脊椎不相关的(不确定它是否是正确的术语。我认为Agda来源称这种事情为“非严格不相关”)这是字面上的擦除计算无关,但不是完全无关的条款。所以你可以定义

data Vec {α} (A : Set α) : ..(n : ℕ) -> Set α where
  [] : Vec A 0
  _∷_ : ∀ ..{n} -> A -> Vec A n -> Vec A (suc n)
在运行时间之前,
n
将被擦除。或者至少它看起来是这样设计的,很难确定,贝卡