【问题标题】:How does the discriminate tactic work?歧视策略如何运作?
【发布时间】:2017-03-23 04:42:02
【问题描述】:

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


    【解决方案1】:

    让我们回顾一下这个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-reduction 的一步,我们会将上述简化为

    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是相等的归纳原理,说equals可以代替equals:

    eq_ind : forall (A : Type) (x : A) (P : A -> Prop),
       P x -> forall y : A, x = y -> P y
    

    换句话说,如果有一个P x 的证明,那么对于所有等于xyP y 成立。

    现在,让我们使用eq_ind 逐步创建False 的证明(最后我们应该得到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 给我们的(模一些包装)。

    【讨论】:

    • 可以说上面的最后一个词是明确“歧视”的最短方法吗?
    • @Cryptostasis 我认为我们可以。或者,我们可以进行一些缩减(使用Eval cbv in fun H : A = B => [the above term].)并到达(fun H : A = B => match H in (_ = y) return match y with | A => True | B => False end with | eq_refl => I end)
    • 是否有打印 discriminate 策略(或任何其他内置策略)的命令?
    • @Cryptostasis 你是什么意思?您想访问它的源代码吗?
    • 我的意思是“Print Ltac”
    【解决方案2】:

    另一个答案侧重于判别部分,我将侧重于手动证明。你试过了:

    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 知道,无论是 nS 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 的定义将索引实例化为与参数相同的值),因此我们声称我们返回了一些@987654367 类型的值@,这里是I。但是从外部的角度来看,c 等于B(因为e 的类型是A=B),而这一次return 子句声称match 返回了一些值输入False。我们在这里使用 Coq 的功能来简化我们刚刚在 test2 中看到的类型中的模式匹配。请注意,我们在除B 之外的其他情况下使用了True,但我们并不特别需要True。我们只需要一些有人居住的类型,这样我们就可以在eq_refl 分支中返回一些东西。

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

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

    【讨论】:

    • This 是(恕我直言)一个很好的答案,它解释了参数和索引之间的区别。
    猜你喜欢
    • 2010-09-10
    • 1970-01-01
    • 2019-05-09
    • 2018-07-17
    • 1970-01-01
    • 2012-08-06
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多