另一个答案侧重于判别部分,我将侧重于手动证明。你试过了:
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 给 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.
这里发生了什么?本质上,Coq 分别对match 和match 本身的分支进行类型检查。在唯一的分支eq_refl 中,c 等于A(因为eq_refl 的定义将索引实例化为与参数相同的值),因此我们声称我们返回了一些@987654367 类型的值@,这里是I。但是从外部的角度来看,c 等于B(因为e 的类型是A=B),而这一次return 子句声称match 返回了一些值输入False。我们在这里使用 Coq 的功能来简化我们刚刚在 test2 中看到的类型中的模式匹配。请注意,我们在除B 之外的其他情况下使用了True,但我们并不特别需要True。我们只需要一些有人居住的类型,这样我们就可以在eq_refl 分支中返回一些东西。
回到 Coq 产生的奇怪术语,Coq 使用的方法做了类似的事情,但在这个例子中,肯定更复杂。特别是,当 Coq 需要无用的类型和术语时,它经常使用由 idProp 占据的类型 IDProp。它们对应于上面使用的True 和I。
最后,我提供了一个关于 coq-club 的讨论 link,它真正帮助我理解了如何在 Coq 中输入扩展模式匹配。