我在Coq的SSReflect扩展中找到了两个似乎特别有用的惯例,但在新的依赖类型语言(Lean,Agda,Idris)中我没有被广泛采用。
首先,可能的谓词表示为布尔返回函数,而不是归纳定义的数据类型。这默认带来可判定性,通过计算开辟了更多的证明机会,并通过避免校对引擎携带大量证明条件来提高检查性能。我看到的主要缺点是需要使用反射引理来在证明时操纵这些布尔谓词。
其次,具有不变量的数据类型被定义为包含简单数据类型和不变量证明的从属记录。例如,固定长度序列在SSReflect中定义,如:
Structure tuple_of : Type := Tuple {tval :> seq T; _ : size tval == n}.
seq
并证明该序列的长度是某个值。这与例如Idris定义了这种类型:
data Vect : (len : Nat) -> (elem : Type) -> Type
依赖类型的数据结构,其中不变量是其类型的一部分。 SSReflect方法的一个优点是它允许重用,因此例如为seq
定义的许多函数和关于它们的证明仍然可以与tuple
一起使用(通过对底层操作) seq
),而与Idris'需要为reverse
重写诸如append
,Vect
之类的方法函数。 Lean实际上在其标准库vector
中具有等效的SSReflect样式,但它也具有Idris样式的array
,它似乎在运行时具有优化的实现。
一个SSReflect-oriented book甚至声称Vect n A
样式方法是反模式:
依赖类型语言中的常见反模式和Coq尤其是将这种代数属性编码为数据类型和函数本身的定义(一个规范示例) 这种方法的长度索引列表)。正如它所展示的那样,这种方法看起来很吸引人 它依赖于类型的强大功能来捕获数据类型和函数的某些属性 本质上是不可扩展的,因为总会有另一种感兴趣的属性,而这种属性并非如此 由数据类型/函数的设计者预见,因此必须将其编码为外部事实 无论如何。这就是为什么我们提倡这种方法,其中数据类型和函数被定义为接近 它们将由程序员尽可能地定义,以及它们的所有必要属性 单独证明。
因此,我的问题是,为什么这些方法没有得到更广泛的采用。我是否缺少缺点,或者他们的优势在具有比Coq更好支持依赖模式匹配的语言中不那么重要?
答案 0 :(得分:5)
我可以提供关于第一点的一些想法(将谓词定义为布尔返回函数)。我对这种方法的最大问题是:根据定义,函数不可能存在错误,即使事实证明它的计算不是你想要计算的。在许多情况下,如果必须在其定义中包含谓词的决策过程的实现细节,它也会模糊谓词的实际含义。
在数学应用程序中,如果你想定义一个谓词,这个谓词通常是不可判定的东西,即使它在你的特定情况下恰好是可判定的,也会有问题。我在这里谈论的一个例子就是用给定的表示来定义组:在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.
答案 1 :(得分:4)
默认情况下,它带来了可判定性,通过计算为证明提供了更多机会,并通过避免校对引擎携带大量证明条件来提高检查性能。
您不必按照Edwin Brady's thesis中的名称"强制优化"来执行大型术语。 Agda确实有强制影响类型检查(特别是计算Universe的方式是相关的),但我不确定在类型检查时使用的东西是否真的在运行时被删除。无论如何,Agda有两个不相关的概念:.(eq : p ≡ q)
通常是不相关的(意味着eq
在类型检查时无关紧要,所以它在定义上等于这种类型的任何其他术语)和..(x : A)
与脊椎无关(不确定它是否是一个正确的术语。我认为Agda来源称之为“#34;非严格无关紧要"”这实际上是为了计算无关的擦除,但不是完全不相关的条款。所以你可以定义
data Vec {α} (A : Set α) : ..(n : ℕ) -> Set α where
[] : Vec A 0
_∷_ : ∀ ..{n} -> A -> Vec A n -> Vec A (suc n)
和n
将在运行时间之前删除。或者至少它似乎是这样设计的,很难确定,因为Agda有很多记录不完整的功能。
您可以在Coq中编写这些零成本证明,因为它也会对Prop
中存在的内容产生不相关性。但是,无关紧要是内容深入到Coq的理论中,而在Agda中它是一个独立的特征,所以它完全可以理解,为什么人们比在Agda中更容易在Coq中找到不相关的东西。
SSReflect的方法的一个优点是它允许重用,因此例如为
seq
定义的许多函数和关于它们的证明仍然可以与tuple
一起使用(通过操作在底层seq
),而与Idris'需要为Vect
重写诸如reverse,append等方法函数。
如果你必须证明属性并且那些证明与索引数据定义的函数具有相同的复杂性,那么它不是真正的重用。完成统一机制的工作并传递明确的证据并应用引理来从length xs ≡ n
(以及suc (length xs) ≡ n
sym
trans
subst
获取xs : List A; length xs ≡ n + m
也很不方便,xs : Vec A (n + m)
以及统一机器在许多情况下可以拯救你的所有其他引理。此外,你通过滥用命题平等而失去一些清晰度:让reverse
代替Vect
并不能提高你的语境的可读性,特别是如果它们很大,通常就是这种情况。另外还有一个问题:有时使用SSReflect方法定义函数会更难:你提到了reverse
List
,我挑战你从头开始定义这个函数(将Data.Vec
作为reverse (reverse xs) ≡ xs
作为"重复使用"部分内容),然后将您的解决方案与Agda标准库中的Vec
中的定义进行比较。如果您默认情况下没有为命题启用不相关性(Agda就是这种情况),那么如果您想证明List
,您还需要证明有关证据的属性。这是很多非平凡的样板。
所以SSReflect的方法并不完美。另一个也是。两者都有改进吗?是的,饰品(见Ornamental Algebras, Algebraic Ornaments和The essence of ornaments)。您可以通过应用相应的观赏代数轻松地从crush
获取:let g:netrw_browser_viewer='open'
,但我无法说明您将从中获得多少代码重用以及类型是否会让您疯狂或不。我听说人们实际上在某个地方使用装饰品。
因此,我们没有理想的SSReflect解决方案,其他人拒绝采用它。希望有更合适的方法来获得实际的代码重用。
<强>更新强>
Anton Trunov 在他们的评论中让我意识到我有点过于敏捷,Coq中的人有很多可以简化校样的策略,因此Coq中的证明通常更容易(提供你有CPDT书中的:let g:netrw_browsex_viewer="setsid xdg-open"
之类的武器,而不是定义数据上的函数。那么,我认为默认证明无关紧要,重型战术机制是使SSReflect的方法在Coq中有效的原因。