歧视策略如何运作?

问题描述 投票:6回答:2

我很好奇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

现在我完全糊涂了。这还是比较复杂的。谁能解释一下这里发生了什么?

coq coq-tactic
2个回答
7
投票

让我们回顾这个l1术语并描述它的每个部分。

l1 : A = B -> False

l1是一个含义,因此通过Curry-Howard对应它是一个抽象(函数):

fun H : A = B =>

现在我们需要构造抽象体,它必须具有类型Falsediscriminate战术选择实施身体作为应用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等于xP 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的前两个论点,我们还需要三个:证明xA的分支,这是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给我们的(模数包装)。


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的定义
  • 完整的模式匹配语法,使用asinreturn术语

首先,我们可以看看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子句可以帮助指定每个分支的返回类型。该子句可以使用模式匹配的asin子句中定义的变量,它们分别绑定匹配的术语和类型索引。 return子句将在每个分支的上下文中解释,使用此上下文替换asin的变量,逐个类型检查分支,并用于从外部角度键入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 ntest都会产生类型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类型分别检查matchmatch本身的分支。在唯一的分支eq_refl中,c等于A(因为eq_refl的定义实例化了与参数相同的值的索引),因此我们声称我们返回了True类型的一些值,这里是I。但是从外部观点来看,c等于B(因为eA=B类型),而这次return条款声称match返回某种类型的False值。我们在这里使用Coq的功能来简化我们在test2中看到的类型中的模式匹配。请注意,我们在True以外的其他情况下使用B,但我们并不特别需要True。我们只需要一些有人居住的类型,这样我们就可以在eq_refl分支中返回一些内容。

回到Coq产生的奇怪术语,Coq使用的方法做了类似的事情,但在这个例子中,肯定更复杂。特别是,Coq经常使用IDProp居住的idProp类型,当它需要无用的类型和术语时。它们对应于上面使用的TrueI

最后,我给了link关于coq-club的讨论,这真的帮助我理解了如何在Coq中输入扩展模式匹配。

© www.soinside.com 2019 - 2024. All rights reserved.