Вот прямой перевод вашей функции в Coq:
From Coq Require Import Arith.
Definition add_two_even (n : {n : nat | Nat.even n = true}) : nat :=
proj1_sig n + 1.
Обратите внимание, что вам нужна функция proj1_sig
для извлечения n
из типа подмножества {n : nat | Nat.even n = true}
. Чтобы использовать add_two_even
, вам также нужно пойти другим путем: перейти от числа к элементу {n | Nat.even n = true}
. В Coq это требует ручного доказательства. Для конкретных значений n
это просто:
(* The next command fails with a type checking error *)
Fail Compute add_two_even (exist _ 1 eq_refl).
(* The next command succeeds *)
Compute add_two_even (exist _ 4 eq_refl).
Конструктор exist
обертывает значение x
с доказательством P x
, создавая элемент типа подмножества {x | P x}
. Термин eq_refl
является доказательством x = x
для любого значения x
. Когда n
- конкретное число, Coq может оценить Nat.even n
и определить, является ли eq_refl
действительным доказательством Nat.even n = true
. Когда n
равен 1
, Nat.even n = false
и проверка не выполняется, что приводит к первому сообщению об ошибке. Когда n
равно 4
, проверка завершается успешно.
Ситуация усложняется, когда n
не константа, а произвольное выражение. Доказательство того, что Nat.even n = true
может потребовать подробных рассуждений, которыми должен руководствоваться пользователь. Например, мы знаем, что Nat.even (n + n) = true
для любого значения n
, но Coq не знает. Таким образом, чтобы вызвать add_two_even
что-то в форме n + n
, нам нужно показать лемму.
Lemma add_n_n_even n : Nat.even (n + n) = true.
Proof.
induction n as [|n IH]; trivial.
now simpl; rewrite <- plus_n_Sm, IH.
Qed.
Definition foo (n : nat) :=
add_two_even (exist _ (n + n) (add_n_n_even n)).
Есть некоторые инструменты для облегчения этого стиля программирования, такие как плагин уравнений, но общий Сообщество Coq считает, что вам следует избегать подмножеств типов для таких функций, как add_two_even
, где ограничения существенно не упрощают свойства функции.
Вы можете найти множество примеров правильного использования подмножеств типов в библиотеках Mathematical Components. Например, библиотеки используют типы подмножества для определения типа n.-tuple T
списков длины n
и типа 'I_n
целых чисел, ограниченных n
. Это позволяет нам определить общую функцию доступа tnth (t : n.-tuple T) (i : 'I_n) : T
, которая извлекает i
-й элемент списка t
. Если мы определяем этот метод доступа для произвольных списков и целых чисел, нам нужно передать значение по умолчанию функции, чтобы она возвращалась, когда индекс выходит за границы, или изменить подпись функции так, чтобы вместо этого она возвращала значение типа option T
, указывая, что функция может не вернуть значение. Это изменение усложняет определение свойств функции доступа. Например, рассмотрим лемму eq_from_tnth
, в которой говорится, что два списка равны, если все их элементы равны:
eq_from_tnth:
forall (n : nat) (T : Type) (t1 t2 : n.-tuple T),
(forall i : 'I_n, tnth t1 i = tnth t2 i) -> t1 = t2
Формулировка этой леммы для произвольных списков усложняется, потому что нам нужно дополнительное предположение о том, что два списка имеют одинаковый размер. (Здесь x0
- значение по умолчанию.)
eq_from_nth:
forall (T : Type) (x0 : T) (s1 s2 : list T),
size s1 = size s2 ->
(forall i : nat, i < size s1 -> nth x0 s1 i = nth x0 s2 i) -> s1 = s2
person
Arthur Azevedo De Amorim
schedule
14.11.2019