我很好奇discriminate战术背后的策略是如何运作的.因此我做了一些实验.
首先是一个简单的归纳定义:
Inductive AB:=A|B.
Run Code Online (Sandbox Code Playgroud)
然后是一个简单的引理,可以通过discriminate策略证明:
Lemma l1: A=B -> False.
intro.
discriminate.
Defined.
Run Code Online (Sandbox Code Playgroud)
让我们看看证明的样子:
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
Run Code Online (Sandbox Code Playgroud)
这看起来相当复杂,我不明白这里发生了什么.因此,我试图更明确地证明相同的引理:
Lemma l2: A=B -> False.
apply (fun e:(A=B) => match e with end).
Defined.
Run Code Online (Sandbox Code Playgroud)
让我们再看看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
Run Code Online (Sandbox Code Playgroud)
现在我完全糊涂了.这还是比较复杂的.谁能解释一下这里发生了什么?
让我们回顾这个l1术语并描述它的每个部分.
l1 : A = B -> False
Run Code Online (Sandbox Code Playgroud)
l1 是一个含义,因此通过Curry-Howard对应它是一个抽象(函数):
fun H : A = B =>
Run Code Online (Sandbox Code Playgroud)
现在我们需要构造抽象体,它必须具有类型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)
Run Code Online (Sandbox Code Playgroud)
如果我们执行beta减少的一个步骤,我们将简化以上内容
False_ind False
(eq_ind A
(fun e : AB => match e with
| A => True
| B => False
end) I B H)
Run Code Online (Sandbox Code Playgroud)
第一个参数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
Run Code Online (Sandbox Code Playgroud)
eq_ind 是平等的归纳原则,说等于可以代替等于:
eq_ind : forall (A : Type) (x : A) (P : A -> Prop),
P x -> forall y : A, x = y -> P y
Run Code Online (Sandbox Code Playgroud)
换句话说,如果一个人拥有证据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
Run Code Online (Sandbox Code Playgroud)
我们对前两个参数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
Run Code Online (Sandbox Code Playgroud)
这正是discriminate给我们的东西(模数包装).
另一个答案集中在歧视部分,我将重点关注手动证明.你试过:
Lemma l2: A=B -> False.
apply (fun e:(A=B) => match e with end).
Defined.
Run Code Online (Sandbox Code Playgroud)
应该注意并使我经常使用Coq感到不舒服的是,Coq接受了错误定义的定义,它在内部重写为良好类型的术语.这样可以减少冗长,因为Coq自己添加了一些部分.但另一方面,Coq操纵的术语与我们输入的术语不同.
您的证明就是这种情况.当然,模式匹配e应该涉及构造函数eq_refl,它是该eq类型的单个构造函数.在这里,Coq检测到相等性没有居住,因此了解如何修改代码,但是您输入的内容不是正确的模式匹配.
两种成分可以帮助理解这里发生的事情:
eqas,in和return术语首先,我们可以看一下定义eq.
Inductive eq {A : Type} (x : A) : A -> Prop := eq_refl : x = x.
Run Code Online (Sandbox Code Playgroud)
请注意,此定义与看起来更自然(在任何情况下,更对称)的定义不同.
Inductive eq {A : Type} : A -> A -> Prop := eq_refl : forall (x:A), x = x.
Run Code Online (Sandbox Code Playgroud)
这是非常重要的,eq用第一个定义而不是第二个定义.特别是,对于我们的问题,最重要的是,在x = y,x是一个参数,同时y是一个指数.也就是说,x在所有构造函数中都是常量,而y在每个构造函数中可以是不同的.你对这种类型有不同的看法Vector.t.如果添加元素,向量元素的类型将不会更改,这就是它作为参数实现的原因.然而,它的大小可以改变,这就是它作为索引实现的原因.
现在,让我们看一下扩展模式匹配语法.我在这里简单解释一下我所理解的内容.请不要犹豫,查看参考手册以获取更安全的信息.该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.
Run Code Online (Sandbox Code Playgroud)
根据值n,我们不会返回相同的类型.类型test是forall n : nat, match n with | 0 => nat | S _ => bool end.但是当Coq可以决定我们匹配的情况时,它可以简化类型.例如:
Definition test2 n : bool := test (S n).
Run Code Online (Sandbox Code Playgroud)
在这里,Coq的人都知道,无论是n,S n给test会导致类型的东西bool.
为了平等,我们可以做类似的事情,这次使用该in子句.
Definition test3 (e:A=B) : False :=
match e in (_ = c) return (match c with | B => False | _ => True end) with
| eq_refl => I
end.
Run Code Online (Sandbox Code Playgroud)
这里发生了什么 ?从本质上讲,Coq类型分别检查match和它match自己的分支.在唯一的分支中eq_refl,c等于A(因为其定义的eq_refl实例化索引具有与参数相同的值),因此我们声称我们返回了一些类型的值True,这里I.但是从外部角度来看,c等于B(因为e是类型A=B),而这一次该return条款声称match返回一些类型的值False.我们在这里使用Coq的功能来简化我们刚才看到的类型中的模式匹配test2.请注意,我们True在其他情况下使用B,但我们并不True特别需要.我们只需要一些有人居住的类型,这样我们就可以在eq_refl分支中返回一些内容.
回到Coq产生的奇怪术语,Coq使用的方法做了类似的事情,但在这个例子中,肯定更复杂.特别是,当Coq 需要无用的类型和术语时,它经常使用IDProp居住的idProp类型.它们对应于True并I略高于使用.
最后,我给出了关于coq-club的讨论链接,这真正帮助我理解了如何在Coq中输入扩展模式匹配.