예, 정렬 루틴에 대해 정확한 유형을 표현할 수 있으므로 해당 유형의 모든 함수가 실제로 입력 목록을 정렬해야합니다.
좀 더 고급스럽고 우아한 솔루션이있을 수 있지만 기본 솔루션 만 스케치하겠습니다.
Coq와 같은 표기법을 사용합니다. 우리 f: nat -> nat
는 순열로 작용할 것을 요구하는 술어를 정의하는 것으로 시작합니다 .0 .. n - 1:
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
없다는를m < n 실제로 보유합니다.
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
이엔 요소가 길면 정렬되고 순열이 있습니다. 0 .. n - 1입력 목록의 요소를 l
출력 목록 의 요소로 매핑합니다 s
. 위의 정리를 호출하여 증명해야합니다.에프( m ) < n에 필요한 nth
.
그러나 사용자, 즉 프로그래머는 정렬 알고리즘이 올바른지 증명해야합니다. 컴파일러는 단순히 정렬이 올바른지 확인하지 않고 제공된 증거를 확인하기 만하면됩니다. 실제로 컴파일러는 그 이상을 수행 할 수 없습니다. "이 프로그램은 정렬 알고리즘입니다"와 같은 시맨틱 특성은 결정 불가능 하므로 (쌀 정리에 의해) 검증 단계를 완전히 자동으로 만들 수는 없습니다.
먼 미래에, 우리는 여전히 자동 정리 프로 바이더가 너무 똑똑 해져서 "가장 많이"실제로 사용되는 알고리즘이 자동으로 올바르게 증명 될 수 있기를 희망 할 수 있습니다. 라이스 정리는 모든 경우에 이것이 이루어질 수는 없다고 말합니다. 우리가 기대할 수있는 것은 정확하고 광범위하게 적용되지만 본질적으로 불완전한 시스템입니다.
마지막으로 간단한 유형의 시스템조차 불완전 하다는 것을 잊었을 때가 있습니다 ! 예를 들어 Java에서도
int f(int x) {
if (x+2 != 2+x)
return "Houston, we have a problem!";
return 42;
}
의미 상 유형 안전 (항상 정수를 리턴 함)이지만 유형 검사기는 도달 할 수없는 리턴에 대해 불평합니다.