我很好奇discriminate
策略如何在幕后工作。因此我做了一些实验。
首先是一个简单的归纳定义:
Inductive AB:=A|B.
然后是一个简单的引理,可以用discriminate
策略证明:
Lemma l1: A=B -> False.
intro.
discriminate.
Defined.
让我们看看证明的样子:
Print l1.
l1 =
fun H : A = B =>
(fun H0 : False => False_ind False H0)
(eq_ind A
(fun e : AB => match e with
| A => True
| B => False
end) I B H)
: A = B -> False
这看起来相当复杂,我不明白这里发生了什么。因此,我试图更明确地证明相同的引理:
Lemma l2: A=B -> False.
apply (fun e:(A=B) => match e with end).
Defined.
让我们再看看Coq用这个做了什么:
Print l2.
l2 =
fun e : A = B =>
match
e as e0 in (_ = a)
return
(match a as x return (A = x -> Type) with
| A => fun _ : A = A => IDProp
| B => fun _ : A = B => False
end e0)
with
| eq_refl => idProp
end
: A = B -> False
现在我完全糊涂了。这还是比较复杂的。 谁能解释一下这里发生了什么?
答案 0 :(得分:7)
让我们回顾这个l1
术语,并描述它的每一部分。
l1 : A = B -> False
l1
是一个含义,因此通过Curry-Howard对应它是一个抽象(函数):
fun H : A = B =>
现在我们需要构造抽象体,它必须具有类型False
。 discriminate
策略选择将主体实现为应用f x
,其中f = fun H0 : False => False_ind False H0
并且它只是False
的归纳原则的包装,它表示如果你有False
的证明,你可以得到你想要的任何命题的证明(False_ind : forall P : Prop, False -> P
):
(fun H0 : False => False_ind False H0)
(eq_ind A
(fun e : AB => match e with
| A => True
| B => False
end) I B H)
如果我们执行beta减少的一个步骤,我们会将上述内容简化为
False_ind False
(eq_ind A
(fun e : AB => match e with
| A => True
| B => False
end) I B H)
False_ind
的第一个参数是我们正在构建的术语的类型。如果你要证明A = B -> True
,那就是False_ind True (eq_ind A ...)
。
顺便说一句,很容易看出我们可以进一步简化我们的身体 - 为False_ind
工作,需要提供False
的证明,但这正是我们想要的在这里建造!因此,我们可以完全摆脱False_ind
,获得以下内容:
eq_ind A
(fun e : AB => match e with
| A => True
| B => False
end) I B H
eq_ind
是平等的归纳原则,说等于可以代替等于:
eq_ind : forall (A : Type) (x : A) (P : A -> Prop),
P x -> forall y : A, x = y -> P y
换句话说,如果有P x
证明,则所有y
等于x
,P y
成立。
现在,让我们逐步创建False
使用eq_ind
的证明(最后我们应该获得eq_ind A (fun e : AB ...)
字词。)
我们首先使用eq_ind
开始,然后将其应用于某些x
- 让我们使用A
来实现此目的。接下来,我们需要谓词P
。写下P
时要记住的一件重要事情是我们必须能够证明P x
。这个目标很容易实现 - 我们将使用True
命题,它有一个简单的证明。要记住的另一件事是我们试图证明的命题(False
) - 如果输入参数不是A
,我们应该返回它。
有了上述所有内容,谓词几乎写出来了:
fun x : AB => match x with
| A => True
| B => False
end
我们有eq_ind
的前两个参数,我们还需要三个:x
为A
的分支的证据,这是True
的证明,即I
。一些y
,它将引导我们得到我们想要证明的命题,即B
,并证明A = B
,在一开始就称为H
这个答案。将这些堆叠在一起我们得到了
eq_ind A
(fun x : AB => match x with
| A => True
| B => False
end)
I
B
H
这正是discriminate
给我们的(模数包装)。
答案 1 :(得分:7)
另一个答案集中在歧视部分,我将重点关注手动证明。你试过了:
Lemma l2: A=B -> False.
apply (fun e:(A=B) => match e with end).
Defined.
应该注意并使我经常使用Coq感到不舒服的是,Coq接受了错误定义的定义,它在内部重写为类型很好的术语。这样可以减少冗长,因为Coq自己添加了一些部分。但另一方面,Coq操纵的术语与我们输入的术语不同。
您的证明就是这种情况。当然,e
上的模式匹配应该包含构造函数eq_refl
,它是eq
类型的单个构造函数。在这里,Coq检测到相等性没有居住,因此理解如何修改代码,但是你输入的内容不是正确的模式匹配。
两种成分可以帮助理解这里发生的事情:
eq
as
,in
和return
条款首先,我们可以看一下eq
的定义。
Inductive eq {A : Type} (x : A) : A -> Prop := eq_refl : x = x.
请注意,此定义与看起来更自然(在任何情况下,更对称)的定义不同。
Inductive eq {A : Type} : A -> A -> Prop := eq_refl : forall (x:A), x = x.
使用第一个定义而不是第二个定义定义eq
非常重要。特别是,对于我们的问题,重要的是,在x = y
中,x
是一个参数,而y
是一个索引。也就是说,x
在所有构造函数中是常量,而y
在每个构造函数中可以是不同的。您与Vector.t
类型的区别相同。如果添加元素,向量元素的类型将不会更改,这就是它作为参数实现的原因。然而,它的大小可以改变,这就是它作为索引实现的原因。
现在,让我们看一下扩展模式匹配语法。我在这里简单解释一下我所理解的内容。不要犹豫,查看the reference manual以获取更安全的信息。 return
子句可以帮助指定每个分支的返回类型。该子句可以使用模式匹配的as
和in
子句中定义的变量,它们分别绑定匹配的术语和类型索引。 return
子句将在每个分支的上下文中解释,使用此上下文替换as
和in
的变量,逐个类型检查分支,并使用从外部角度输入match
。
这是一个带有as
子句的人为例子:
Definition test n :=
match n as n0 return (match n0 with | 0 => nat | S _ => bool end) with
| 0 => 17
| _ => true
end.
根据n
的值,我们不会返回相同的类型。 test
的类型为forall n : nat, match n with | 0 => nat | S _ => bool end
。但是当Coq可以决定我们匹配的情况时,它可以简化类型。例如:
Definition test2 n : bool := test (S n).
在这里,Coq知道,无论n
是什么,S n
的{{1}}都会产生test
类型的内容。
为了平等,我们可以使用bool
子句做类似的事情。
in
这里发生了什么?从本质上讲,Coq类型分别检查Definition test3 (e:A=B) : False :=
match e in (_ = c) return (match c with | B => False | _ => True end) with
| eq_refl => I
end.
和match
本身的分支。在唯一的分支match
中,eq_refl
等于c
(因为A
的定义实例化了与参数具有相同值的索引),因此我们声称我们返回了eq_refl
类型的值,此处为True
。但从外部角度来看,I
等于c
(因为B
的类型为e
),而这次是A=B
子句声称return
返回match
类型的某些值。我们在这里使用Coq的功能来简化我们刚才用False
看到的类型中的模式匹配。请注意,我们在test2
以外的其他情况下使用了True
,但我们并不特别需要B
。我们只需要一些有人居住的类型,这样我们就可以在True
分支中返回一些内容。
回到Coq产生的奇怪术语,Coq使用的方法做了类似的事情,但在这个例子中,肯定更复杂。特别是,当Coq需要无用的类型和术语时,它经常使用eq_refl
所居住的类型IDProp
。它们对应于上面使用的idProp
和True
。
最后,我给出了关于coq-club的link讨论,这真的帮助我理解了如何在Coq中输入扩展模式匹配。