Answers:
是的,可以为排序例程表达一个精确的类型,这样具有该类型的任何函数都必须确实对输入列表进行排序。
虽然可能会有一个更高级,更优雅的解决方案,但我只会画一个简单的解决方案。
f: nat -> nat
Definition permutation (n: nat) (f: nat -> nat): Prop :=
(* once restricted, its codomain is 0..n-1 *)
(forall m, m < n -> f m < n) /\
(* it is injective, hence surjective *)
(forall m1 m2, m1 < n -> m2 < n -> f m1 = f m2 -> m1 = m2) .
一个简单的引理可以很容易地证明。
Lemma lem1: forall n f, permutation n f -> m < n -> f m < n.
... (* from the def *)
h
Definition nth {A} {n} (l: list A n) m (h : m < n): A :=
... (* recursion over n *)
给定的排序A
,我们可以表示列表已排序:
Definition ordering (A: Type) :=
{ leq: A->A->bool |
(* axioms for ordering *)
(forall a, leq a a = true) /\
(forall a b c, leq a b = true -> leq b c = true -> leq a c = true) /\
(forall a b, leq a b = true -> leq b a = true -> a = b)
} .
Definition sorted {A} {n} (o: ordering A) (l: list A n): Prop :=
...
最后,这是排序算法的类型:
Definition mysort (A: Type) (o: ordering A) (n: nat) (l: list A n):
{s: list A n | sorted o s /\
exists f (p: permutation n f),
forall (m: nat) (h: m < n),
nth l m h = nth s (f m) (lem1 n f p h) } :=
... (* the sorting algorithm, and a certificate for its output *)
s
l
s
nth
但是请注意,必须由用户(即程序员)来证明其排序算法正确。编译器不会简单地验证排序是否正确:它所做的只是检查提供的证明。确实,编译器不能做更多的事情:诸如“该程序是一种排序算法”之类的语义属性是不确定的(根据莱斯定理),因此我们不能希望使证明步骤完全自动化。
在遥远的将来,我们仍然希望自动定理证明变得如此聪明,以至于可以自动证明“最”实用的算法是正确的。赖斯定理仅指出并非在所有情况下都可以这样做。我们所希望的只是一个正确,可广泛应用但本质上不完整的系统。
最后一点,有时甚至忘记了简单的类型系统也是不完整的!例如,即使在Java中
int f(int x) {
if (x+2 != 2+x)
return "Houston, we have a problem!";
return 42;
}
从语义上讲是安全的类型(它总是返回整数),但是类型检查器将抱怨无法到达的返回。
Twan van Laarhoven 在Agda中有一个很好的完整示例,它是“ mergesort,插入排序和选择排序的正确性和运行时”。
这些评论也很有趣:在其中,提到了鲍勃·阿特基(Bob Atkey)涉及深度嵌入线性逻辑的解决方案。