Так, можна висловити точний тип для процедури сортування, так що будь-яка функція, що має цей тип, дійсно повинна сортувати список введення.
Хоча може бути більш просунуте і елегантне рішення, я начеркну лише елементарне.
Ми будемо використовувати позначення, подібні кок. Почнемо з визначення присудка, який вимагає, що f: nat -> nat
діє як перестановка на :0 .. п - 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 .. п - 1l
s
f( м ) < nnth
Однак зауважте, що саме користувач, тобто програміст, повинен довести їх алгоритм сортування правильним. Компілятор не просто перевірить правильність сортування: все, що він робить, це перевірка наданого доказу. Дійсно, компілятор не може зробити більше того: семантичні властивості, такі як "ця програма є алгоритмом сортування", не можна визначити (за теоремою Райса), тому ми не можемо сподіватися зробити крок доведення повністю автоматичним.
У далекому, далекому майбутньому ми все ще можемо сподіватися, що докази автоматичної теореми стануть такими розумними, що "більшість" практично використаних алгоритмів можуть бути автоматично доведені правильними. Теорема Райса стверджує лише, що цього робити не можна у всіх випадках. Все, на що ми можемо сподіватися, - це правильна, широко застосовувана, але по суті неповна система.
На завершення іноді забувається, що навіть прості системи типу є неповними ! Наприклад, навіть на Java
int f(int x) {
if (x+2 != 2+x)
return "Houston, we have a problem!";
return 42;
}
це семантично безпечний тип (він завжди повертає ціле число), але перевіряючи тип буде скаржитися на недоступний повернення.