我试图熟悉Coq中的子集类型,而且我遇到过一种我不知道如何继续操作的用例。考虑一个期望自然数大于2的函数,以及另一个期望自然数大于2且小于10的函数。在后一个函数中,我想调用前者,这应该是可能的,但它似乎我必须做一些事情,使其类型适合功能应用。这就是我想要做的事情:
Definition add_one_if_gt_2 (i : {i1 : nat | i1 > 2}) := (proj1_sig i) + 1.
Definition add_one_if_gt_2_and_lt_10 (i : {i1 : nat | i1 > 2 /\ i1 < 10}) := add_one_if_gt_2 i.
add_one_if_gt_2_and_lt_10
中缺少哪些东西来进行类型检查?通常,对于可能兼容的更复杂的子集类型,通常的方法是什么?
此外,我在子集类型规范中使用i1
以防万一,但是如果我使用i
它似乎也有效,尽管我不知道是否有&{1}}由于参数名称也是i
,因此存在任何潜在问题。使用i
两个变量会有问题吗?
答案 0 :(得分:2)
一种可能性是使用Program
功能:
Require Import Program.
Program Definition add_one_if_gt_2 (i : {i1 : nat | i1 > 2}) :=
i + 1.
Program Definition add_one_if_gt_2_and_lt_10
(i : {i1 : nat | i1 > 2 /\ i1 < 10}) :=
add_one_if_gt_2 i.
如果您单步执行此代码,您会注意到系统正在生成有关“义务”的消息 - 但在此示例中,所有义务都很简单,系统可以自行解决这些义务,以及定义马上完成了。 (在这种情况下,第二个定义产生的义务是i > 2 /\ i < 10
暗示i > 2
。)
在其他无法轻易解决义务的情况下,您可能会获得待定义务。在这种情况下,您可以使用诸如Next Obligation.
之类的Gallina指令进入证明模式并自行证明。 (有关更多信息,请参阅Coq文档的相应章节。)
答案 1 :(得分:1)
您可以手动执行此操作,因为它不难破坏{i1 : nat | i1 > 2 /\ i1 < 10}
类型的术语,该术语基本上是nat
(i1
)和两个样张的三倍:{ {1}}和i1 > 2
。我们要做的是破坏这个三元组,丢弃第三个组件并将其重新打包成i2 < 10
类型的术语,这是一对:
{i1 : nat | i1 > 2}
我们也可以通过预测来做到这一点:
Definition add_one_if_gt_2 (i : {i1 : nat | i1 > 2}) :=
S (proj1_sig i).
Definition add_one_if_gt_2_and_lt_10 (i : {i1 : nat | i1 > 2 /\ i1 < 10}) :=
let '(exist _ i1 (conj i1_gt2 _)) := i in
add_one_if_gt_2 (exist _ i1 i1_gt2).
另一种方法是在您的特殊情况下使用Definition add_one_if_gt_2_and_lt_10' (i : {i1 : nat | i1 > 2 /\ i1 < 10}) :=
add_one_if_gt_2 (exist _ (proj1_sig i)
(proj1 (proj2_sig i))).
类型:
sig2
使用强制机制,您可以将上述定义简化为:
Definition add_one_if_gt_2_and_lt_10_sig2 (i : {i1 : nat | i1 > 2 & i1 < 10}) :=
add_one_if_gt_2 (sig_of_sig2 i).
这正是您尝试应用的解决方案!
如果你不熟悉强制:如果遇到可以修补的类型错误,这个机制会自动插入Coercion sig_of_sig2 : sig2 >-> sig.
Definition add_one_if_gt_2_and_lt_10_sig2' (i : {i1 : nat | i1 > 2 & i1 < 10}) :=
add_one_if_gt_2 i.
(或其他一些合适的强制)。