Проверка операции сортировки в системе типов

9

Я хочу знать, насколько полезна система типов в языке программирования. Например, я знаю, что на языке программирования с зависимой типизацией мы можем создать Vectorкласс, включающий размер вектора в сигнатуру типа. Это как фактический пример. Мы также можем написать функцию, appendиспользуя эти сигнатуры, чтобы компилятор доказал, что размер результирующего списка будет суммой входных списков.

Есть ли способ кодировать, например, в сигнатуре типа алгоритма сортировки, чтобы компилятор гарантировал, что результирующий список будет перестановкой входного списка? Как это можно сделать, если это возможно?

meguli
источник

Ответы:

13

Да, для процедуры сортировки можно выразить точный тип, так что любая функция, имеющая этот тип, должна действительно сортировать список ввода.

Хотя может быть более продвинутое и изящное решение, я нарисую только простое.

Мы будем использовать 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 *)

Мы определяем, какой й элемент списка имеет длину . Эта функция требует доказательства того , что действительно выполняется.мNhм<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 *)

sN0 ..N-1lsе(м)<Nnth

Обратите внимание, что именно пользователь, то есть программист, должен доказать правильность алгоритма сортировки. Компилятор не просто проверит правильность сортировки: он лишь проверяет предоставленное доказательство. Действительно, компилятор не может сделать намного больше: семантические свойства, такие как «эта программа является алгоритмом сортировки», неразрешимы (по теореме Райса), поэтому мы не можем надеяться сделать шаг проверки полностью автоматическим.

В далеком, далеком будущем мы все еще можем надеяться, что автоматические средства проверки теорем станут настолько умными, что «большинство» практически используемых алгоритмов могут быть автоматически доказаны правильными. Теорема Райса только утверждает, что это не может быть сделано во всех случаях. Все, на что мы можем надеяться, - это правильная, широко применимая, но изначально неполная система.

И последнее замечание: иногда забывают, что даже простые системы типов неполны ! Например, даже на Яве

int f(int x) {
   if (x+2 != 2+x)
      return "Houston, we have a problem!";
   return 42;
}

является семантически безопасным типом (он всегда возвращает целое число), но средство проверки типов будет жаловаться на недостижимый возврат.

чи
источник
7

У Twan van Laarhoven есть хороший полностью проработанный пример в Agda : «Правильность и время выполнения сортировки слиянием, сортировки вставкой и сортировкой выбора».

Комментарии также интересны: в них, например, упоминается решение Боба Атки, включающее глубокое встраивание линейной логики .

Gallais
источник