Coq:构建可判定 sigma 类型成员的最简单方法?
Coq: easiest way to construct members of a decidable sigma type?
考虑以下玩具开发:
Inductive IsEven: nat -> Prop :=
| is_even_z : IsEven 0
| is_even_S : forall n, IsEven n -> IsEven (S (S n)).
Definition EvenNat := {n | IsEven n}.
我想创建一个函数,给定一个我个人认为是偶数的数字,returns 类型 EvenNat
的相应值,无需大惊小怪。
Example fortyTwo : EvenNat := mkEven 42.
我不确切知道你的假设是什么,但我想说的一种方法是使用 classes:
类型的自动化功能
Require Import Coq.Init.Specif.
Inductive IsEven : nat -> Prop :=
| is_even_z : IsEven 0
| is_even_S : forall n, IsEven n -> IsEven (S (S n)).
Class EvenClass n :=
is_even : IsEven n.
Definition EvenNat := { n | EvenClass n }.
Lemma EvenClass_IsEven :
forall n, EvenClass n -> IsEven n.
Proof.
intros n h.
exact h.
Qed.
#[export] Hint Extern 1 (EvenClass 0) =>
apply is_even_z
: typeclass_instances.
#[export] Hint Extern 1 (EvenClass (S (S ?n))) =>
apply is_even_S ; apply EvenClass_IsEven
: typeclass_instances.
#[export] Hint Extern 1 (EvenClass (proj1_sig ?x)) =>
apply proj2_sig
: typeclass_instances.
#[export] Hint Extern 4 (EvenClass ?n) =>
my_decision_procedure
: typeclass_instances.
我没有定义实例,而是使用 Hint Extern
机制来注入我想要解决目标的任何策略。假设你有一个策略来决定你的 属性 你可以在那里使用它。
自然数表示每个提示的“成本”,因此搜索将首先尝试应用成本最低的提示。
一旦你有了这个,你想要的功能就是
Definition mkEven (n : nat) {h : EvenClass n} : EvenNat :=
exist _ n h.
然后只需写入 mkEven n
就会触发上述提示所述的证明搜索。
Definition ev := mkEven 42.
为了详细说明 Yves 的答案和我的答案之间的区别,我选择使用提示而不是 class 个实例,这样证明搜索就不会太贪心。
例如,当我真的想证明 EvenClass 0
时,我强制搜索只使用 is_even_z
,如下例所示:
Goal exists n, EvenClass n.
Proof.
eexists. Fail exact _.
Abort.
#[export] Instance foo : EvenClass 0.
Proof.
exact _.
Qed.
Goal exists n, EvenClass n.
Proof.
eexists. exact _.
Qed.
如您所见,在第一种情况下,我们将 EvenClass ?n
作为目标,因此证明搜索没有找到任何候选人。
但是,如果我们将 0
案例添加为类型 class 实例,则通过统一 ?n
和 0
证明搜索将成功。
我认为这对于手头的案例来说有点过分了,但是这两种解决方案都有它们的应用。
这个问题,正如最初所说,有一个明显的答案。
问题 A: 如果您的上下文中有一个 nat
类型的数字 n
和一个带有语句 IsEven n
,如何构造EvenNat
类型的对象?
你可以简单地构造一个EvenNat
类型的对象,写下下面的表达式
exist _ n knowledge
或者,如果你真的想要明确:
exist IsEven n knowledge
这体现在下面的脚本中
Inductive IsEven: nat -> Prop :=
| is_even_z : IsEven 0
| is_even_S : forall n, IsEven n -> IsEven (S (S n)).
Definition EvenNat := {n | IsEven n}.
Definition mkEven (n : nat) (knowledge : IsEven n) : EvenNat :=
exist IsEven n knowledge.
题的另一种解读如下:
问题 B: 你如何从一个已知的常量(比如 42)构造一个 EvenNat
类型的对象,这个常量在肉眼看来显然是偶数?
这是我对 Theo Winterhalter 草拟的解决方案的详细阐述,完整使用了 classes 类型的设备。
Require Import Coq.Init.Specif.
Inductive IsEven: nat -> Prop :=
| is_even_z : IsEven 0
| is_even_S : forall n, IsEven n -> IsEven (S (S n)).
Definition EvenNat := {n | IsEven n}.
Definition makeEvenNat (n : nat) (knowledge : IsEven n) : EvenNat :=
exist IsEven n knowledge.
Class EvenClass n : Prop :=
is_even : IsEven n.
#[export] Instance evenClass0 : EvenClass 0.
Proof.
apply is_even_z.
Qed.
#[export] Instance evenClassS n {h : EvenClass n} : EvenClass (S (S n)).
Proof.
apply is_even_S; exact h.
Qed.
(* Using curly braces for the second argument h here deserves an
explanation. *)
Definition mkEven (n : nat) {h : EvenClass n} : EvenNat :=
exist IsEven n h.
Definition ev42 := mkEven 42.
Lemma ev42P : proj1_sig ev42 = 42.
Proof. easy. Qed.
Lemma IsEven42 : IsEven 42.
Proof. exact (proj2_sig ev42). Qed.
mkEven
定义中使用大括号的解释
是 mkEven
实际上需要 2 个参数,但是第二个
不是用户编写的:它必须使用类型 class 推理机制自动构造。此证明必须成功才能真正定义对象。
ev42
的定义就是问题B的答案。请注意,依赖于 mkEven 3
的定义将失败,因为无法找到 3 为偶数的证据。
Fail Definition ev3 := mkEven 3.
此代码已使用 coq.8.15.0 进行测试。
考虑以下玩具开发:
Inductive IsEven: nat -> Prop :=
| is_even_z : IsEven 0
| is_even_S : forall n, IsEven n -> IsEven (S (S n)).
Definition EvenNat := {n | IsEven n}.
我想创建一个函数,给定一个我个人认为是偶数的数字,returns 类型 EvenNat
的相应值,无需大惊小怪。
Example fortyTwo : EvenNat := mkEven 42.
我不确切知道你的假设是什么,但我想说的一种方法是使用 classes:
类型的自动化功能Require Import Coq.Init.Specif.
Inductive IsEven : nat -> Prop :=
| is_even_z : IsEven 0
| is_even_S : forall n, IsEven n -> IsEven (S (S n)).
Class EvenClass n :=
is_even : IsEven n.
Definition EvenNat := { n | EvenClass n }.
Lemma EvenClass_IsEven :
forall n, EvenClass n -> IsEven n.
Proof.
intros n h.
exact h.
Qed.
#[export] Hint Extern 1 (EvenClass 0) =>
apply is_even_z
: typeclass_instances.
#[export] Hint Extern 1 (EvenClass (S (S ?n))) =>
apply is_even_S ; apply EvenClass_IsEven
: typeclass_instances.
#[export] Hint Extern 1 (EvenClass (proj1_sig ?x)) =>
apply proj2_sig
: typeclass_instances.
#[export] Hint Extern 4 (EvenClass ?n) =>
my_decision_procedure
: typeclass_instances.
我没有定义实例,而是使用 Hint Extern
机制来注入我想要解决目标的任何策略。假设你有一个策略来决定你的 属性 你可以在那里使用它。
自然数表示每个提示的“成本”,因此搜索将首先尝试应用成本最低的提示。
一旦你有了这个,你想要的功能就是
Definition mkEven (n : nat) {h : EvenClass n} : EvenNat :=
exist _ n h.
然后只需写入 mkEven n
就会触发上述提示所述的证明搜索。
Definition ev := mkEven 42.
为了详细说明 Yves 的答案和我的答案之间的区别,我选择使用提示而不是 class 个实例,这样证明搜索就不会太贪心。
例如,当我真的想证明 EvenClass 0
时,我强制搜索只使用 is_even_z
,如下例所示:
Goal exists n, EvenClass n.
Proof.
eexists. Fail exact _.
Abort.
#[export] Instance foo : EvenClass 0.
Proof.
exact _.
Qed.
Goal exists n, EvenClass n.
Proof.
eexists. exact _.
Qed.
如您所见,在第一种情况下,我们将 EvenClass ?n
作为目标,因此证明搜索没有找到任何候选人。
但是,如果我们将 0
案例添加为类型 class 实例,则通过统一 ?n
和 0
证明搜索将成功。
我认为这对于手头的案例来说有点过分了,但是这两种解决方案都有它们的应用。
这个问题,正如最初所说,有一个明显的答案。
问题 A: 如果您的上下文中有一个 nat
类型的数字 n
和一个带有语句 IsEven n
,如何构造EvenNat
类型的对象?
你可以简单地构造一个EvenNat
类型的对象,写下下面的表达式
exist _ n knowledge
或者,如果你真的想要明确:
exist IsEven n knowledge
这体现在下面的脚本中
Inductive IsEven: nat -> Prop :=
| is_even_z : IsEven 0
| is_even_S : forall n, IsEven n -> IsEven (S (S n)).
Definition EvenNat := {n | IsEven n}.
Definition mkEven (n : nat) (knowledge : IsEven n) : EvenNat :=
exist IsEven n knowledge.
题的另一种解读如下:
问题 B: 你如何从一个已知的常量(比如 42)构造一个 EvenNat
类型的对象,这个常量在肉眼看来显然是偶数?
这是我对 Theo Winterhalter 草拟的解决方案的详细阐述,完整使用了 classes 类型的设备。
Require Import Coq.Init.Specif.
Inductive IsEven: nat -> Prop :=
| is_even_z : IsEven 0
| is_even_S : forall n, IsEven n -> IsEven (S (S n)).
Definition EvenNat := {n | IsEven n}.
Definition makeEvenNat (n : nat) (knowledge : IsEven n) : EvenNat :=
exist IsEven n knowledge.
Class EvenClass n : Prop :=
is_even : IsEven n.
#[export] Instance evenClass0 : EvenClass 0.
Proof.
apply is_even_z.
Qed.
#[export] Instance evenClassS n {h : EvenClass n} : EvenClass (S (S n)).
Proof.
apply is_even_S; exact h.
Qed.
(* Using curly braces for the second argument h here deserves an
explanation. *)
Definition mkEven (n : nat) {h : EvenClass n} : EvenNat :=
exist IsEven n h.
Definition ev42 := mkEven 42.
Lemma ev42P : proj1_sig ev42 = 42.
Proof. easy. Qed.
Lemma IsEven42 : IsEven 42.
Proof. exact (proj2_sig ev42). Qed.
mkEven
定义中使用大括号的解释
是 mkEven
实际上需要 2 个参数,但是第二个
不是用户编写的:它必须使用类型 class 推理机制自动构造。此证明必须成功才能真正定义对象。
ev42
的定义就是问题B的答案。请注意,依赖于 mkEven 3
的定义将失败,因为无法找到 3 为偶数的证据。
Fail Definition ev3 := mkEven 3.
此代码已使用 coq.8.15.0 进行测试。