Роль пруфов в извлечении Coq

Я пытаюсь понять, какова роль доказательств в извлечении Coq. У меня есть следующий пример целочисленного деления пола на два, взятый из здесь. Для моей первой попытки я использовал ключевое слово Admitted:

(*********************)
(* div_2_even_number *)
(*********************)
Definition div_2_even_number: forall n,
  (Nat.Even n) -> {p:nat | n=p+p}.
Proof.
Admitted.

(*************)
(* test_even *)
(*************)
Definition test_even: forall n,
  {Nat.Even n}+{Nat.Even (pred n)}.
Proof.
Admitted.

(********************)
(* div_2_any_number *)
(********************)
Definition div_2_any_number (n:nat):
  {p:nat | n = p+p}+{p:nat | (pred n) = p+p} :=
  match (test_even n) with
  | left h => inl _ (div_2_even_number n h)
  | right h' => inr _ (div_2_even_number (pred n) h')
  end.

(***************************)
(* Extract to Haskell file *)
(***************************)
Extraction "/home/oren/some_file.hs" div_2_any_number.

Когда я изучаю получившийся файл Haskell, я вижу, что он действительно отсутствует:

div_2_even_number :: Prelude.Integer -> Prelude.Integer
div_2_even_number =
  Prelude.error "AXIOM TO BE REALIZED"

test_even :: Prelude.Integer -> Prelude.Bool
test_even =
  Prelude.error "AXIOM TO BE REALIZED"

div_2_any_number :: Prelude.Integer -> Prelude.Either Prelude.Integer
                    Prelude.Integer
div_2_any_number n =
  case test_even n of {
   Prelude.True -> Prelude.Left (div_2_even_number n);
   Prelude.False -> Prelude.Right (div_2_even_number (pred n))}

Итак, я подумал, хорошо, давайте докажем div_2_even_number:

(*********************)
(* div_2_even_number *)
(*********************)
Definition div_2_even_number: forall n,
  (Nat.Even n) -> {p:nat | n=p+p}.
Proof.
  intros n0 H.
  unfold Nat.Even in H.
  destruct H as [m0].
  exists m0.
Qed.

Но я получаю следующую ошибку:

Error: Case analysis on sort Set is not allowed for inductive definition ex.

Что тут происходит? Я явно что-то здесь упускаю.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
0
247
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы работаете с типами разных видов.

> Check (Nat.Even 8).
Nat.Even 8
     : Prop

> Check {p:nat | 8=p+p}.
{p : nat | 8 = p + p}
     : Set

Особенностью системы типов Coq является то, что вы не можете исключить значение, тип которого находится в Prop, чтобы получить что-то, чей тип не находится в Prop (грубо говоря, Coq делает некоторые исключения для Prop типов, которые не несут информации, таких как True и False, но мы не в том случае). Грубо говоря, вы не можете использовать доказательство предложения ни для чего, кроме как для доказательства другого предложения.

Это ограничение, к сожалению, необходимо, чтобы Prop было непредикативным (мы хотим, чтобы forall P: Prop, P->P был типом в сортировке Prop) и соответствовало закону исключенного третьего. У нас не может быть всего, иначе мы столкнемся с парадоксом Берарди.

Разве вы не можете, например, использовать информацию Prop, чтобы показать, что случай, которого вы достигли, невозможен при построении Type?

luqui 26.04.2019 14:57

@luqui Я не могу вспомнить, но я думаю, что Coq действительно делает исключение для индуктивных типов, имеющих ноль или один тривиальный конструктор, поскольку они не несут «данных». Быстрый эксперимент, кажется, подтверждает это.

chi 26.04.2019 15:09

Я все еще немного запутался, значит ли это, что div_2_even_number можно доказать нет? есть ли способ определить (и извлечь) div_2_any_number?

OrenIshShalom 26.04.2019 15:45

@OrenIshShalom, как определяется Nat.Even?

luqui 26.04.2019 16:42
div_2_even_number можно доказать, но не напрямую из Nat.Even предположения. Подумайте, во что она будет извлечена: если вы удалите доказательства (Nat.Even и n = p + p), у вас останется функция nat -> nat, которая должна отображать четные числа в их половины и которая никогда не будет применяться к нечетным числам (потому что у них нет доказательства Nat.Even на исходном языке). Следовательно, div_2_even_number должен выполнить как минимум столько же вычислений, сколько и деление на два, чтобы получить результат p : nat, плюс, возможно, дополнительную работу по построению доказательного термина для n = p + p.
Li-yao Xia 26.04.2019 16:43

@chi верен, если ожидаемый тип имеет вид Type, вы можете разрушить только неинформативные термины доказательства, и это позволяет вам разрушить доказательства False в «невозможных случаях».

Li-yao Xia 26.04.2019 16:45

@luqui вот оно: Nat.Even = fun n : nat => exists m : nat, n = 2 * m : nat -> Prop

OrenIshShalom 26.04.2019 16:58
Ответ принят как подходящий

Хотя то, что сказал Чи, верно, в этом случае вы действительно можете извлечь свидетеля p из доказательства существования. Когда у вас есть логический предикат P : nat -> bool, если exists p, P p = true, вы можете вычислить некоторое p, которое удовлетворяет предикату, запустив следующую функцию из 0:

find p := if P p then p else find (S p)

Вы не можете написать эту функцию непосредственно в Coq, но это можно сделать, создав специальное индуктивное предложение. Этот паттерн реализован в модуль выбора библиотеки математических компонентов:

From mathcomp Require Import ssreflect ssrfun ssrbool ssrnat eqtype choice.

(* == is the boolean equality test *)
Definition even n := exists p, (n == 2 * p) = true.

Definition div_2_even_number n (nP : even n) : {p | (n == 2 * p) = true} :=
  Sub (xchoose nP) (xchooseP nP).

Функция xchoose : (exists n, P n = true) -> nat выполняет описанный выше поиск, а xchooseP показывает, что ее результат удовлетворяет логическому предикату. (Настоящие типы более общие, но при создании экземпляра в nat они дают эту подпись.) Я использовал логический оператор равенства для упрощения кода, но вместо него можно было бы использовать =.

При этом, если вы заботитесь о запуске своего кода, программирование таким образом ужасно неэффективно: вам нужно выполнять n / 2nat сравнения, чтобы проверить деление n. Гораздо лучше написать просто типизированную версию функции деления:

Fixpoint div2 n :=
  match n with
  | 0 | 1 => 0
  | S (S n) => S (div2 n)
  end.

Другие вопросы по теме