【问题标题】:When does the termination checker reduce a record accessor终止检查器何时减少记录访问器
【发布时间】:2017-11-02 19:01:52
【问题描述】:

我对 Coq 的终止检查器的行为感到困惑,我无法向自己解释。考虑:

Require Import Coq.Lists.List.

Record C a := {  P : a -> bool }.

Arguments P {_}.

Definition list_P {a} (a_C : C a) : list a -> bool := existsb (P a_C).

Definition list_C  {a} (a_C : C a) : C (list a) := {| P := list_P a_C |}.

(* Note that *)
Eval cbn in       fun a C => (P (list_C C)).
(* evaluates to:  fun a C  => list_P C *)

Inductive tree a := Node : a -> list (tree a) -> tree a.

(* Works, using a local record *)
Fixpoint tree_P1 {a} (a_C : C a) (t : tree a) : bool :=
    let tree_C := Build_C _ (tree_P1 a_C) in
    let list_C' := Build_C _ (list_P tree_C) in
    match t with Node _ x ts => orb (P a_C x) (P list_C' ts) end.

(* Works too, using list_P directly *)
Fixpoint tree_P2 {a} (a_C : C a) (t : tree a) : bool :=
    let tree_C := Build_C _ (tree_P2 a_C) in
    match t with Node _ x ts => orb (P a_C x) (list_P tree_C ts) end.

(* Does not work, using a globally defined record. Why not? *)
Fixpoint tree_P3 {a} (a_C : C a) (t : tree a) : bool :=
    let tree_C := Build_C _ (tree_P3 a_C) in
    match t with Node _ x ts => orb (P a_C x) (P (list_C tree_C) ts) end.

第一个和第二个例子表明,当试图了解一个固定点是否正在终止时,Coq 能够解析记录访问器,基本上评估我们在 tree_P1 中写的内容与我们在 tree_P2 中写的内容。

但这似乎只有在本地构建记录 (let tree_C :=…) 时才有效,而不是在使用 Definition 定义时才有效。

但是Fixpoint 可以很好地查看其他定义,例如通过list_P。那么记录有什么特别之处,我可以让 Coq 接受tree_P3吗?

【问题讨论】:

    标签: coq termination


    【解决方案1】:

    在阅读了一些 Coq 中的终止检查器之后,我想我找到了解决方案:

    终止检查器将始终展开本地定义和 beta-reduce。这就是tree_P1 起作用的原因。

    如有必要,终止检查器还将展开被调用的定义(如 list_C'Pexistsb),这就是 tree_P2 起作用的原因。

    然而,终止检查器不会展开出现在match … with 子句中的定义,例如list_C。这是一个最小的例子:

    (* works *)
    Fixpoint foo1 (n : nat) : nat :=
      let t := Some True in 
      match Some True with | Some True => 0
                           | None => foo1 n end.
    
    (* works *)
    Fixpoint foo2 (n : nat) : nat :=
      let t := Some True in 
      match t with | Some True => 0
                   | None => foo2 n end.
    
    (* does not work *)
    Definition t := Some True.
    
    Fixpoint foo3 (n : nat) : nat :=
      match t with | Some True => 0
                   | None => foo3 n end.
    

    原始代码的解决方法是确保调用所有定义(而不是模式匹配),以确保终止检查器将展开它们。我们可以通过切换到延续传递样式来做到这一点:

    Require Import Coq.Lists.List.
    
    Record C_dict a := {  P' : a -> bool }.
    
    Definition C a : Type := forall r, (C_dict a -> r) -> r.
    
    Definition P {a} (a_C : C a) : a -> bool :=
      a_C _ (P' _).
    
    Definition list_P {a} (a_C : C a) : list a -> bool := existsb (P a_C).
    
    Definition list_C  {a} (a_C : C a) : C (list a) :=
       fun _ k => k {| P' := list_P a_C |}.
    
    Inductive tree a := Node : a -> list (tree a) -> tree a.
    
    (* Works now! *)
    Fixpoint tree_P1 {a} (a_C : C a) (t : tree a) : bool :=
        let tree_C := fun _ k => k (Build_C_dict _ (tree_P1 a_C)) in
        match t with Node _ x ts => orb (P a_C x) (P (list_C tree_C) ts) end.
    

    这甚至适用于类型类,因为类型类解析与这些问题无关:

    Require Import Coq.Lists.List.
    
    Record C_dict a := {  P' : a -> bool }.
    
    Definition C a : Type := forall r, (C_dict a -> r) -> r.
    Existing Class C.
    
    Definition P {a} {a_C : C a} : a -> bool := a_C _ (P' _).
    
    Definition list_P {a} `{C a} : list a -> bool := existsb P.
    
    Instance list_C  {a} (a_C : C a) : C (list a) :=
       fun _ k => k {| P' := list_P |}.
    
    Inductive tree a := Node : a -> list (tree a) -> tree a.
    
    (* Works now! *)
    Fixpoint tree_P1 {a} (a_C : C a) (t : tree a) : bool :=
        let tree_C : C (tree a) := fun _ k => k (Build_C_dict _ (tree_P1 a_C)) in
        match t with Node _ x ts => orb (P x) (P ts) end.
    

    【讨论】:

      【解决方案2】:

      对于问题1。我相信在tree_P1中,类实例的定义是在fix构造内部,在终止检查时减少。

      正如您正确指出的那样,以下定义被拒绝。

      Fixpoint tree_P1' {a} `{C a} (t : tree a) : bool :=
          let tree_C := Build_C _ tree_P1' in
          match t with Node _ x ts => orb (P x) (@P _ (* mark *) _ ts) end.
      

      在此定义中,注释 (* mark *) 之后所需的类实例由您在第 7 行的定义填充。但此定义位于 fix 构造之外,不会被终止检查器减少以相同的方式。因此,未应用于任何树参数的 tree_P1' 的出现将保留在代码中,并且终止检查器将无法确定此出现仅用于小于初始参数的参数.

      这是一个疯狂的猜测,因为我们看不到被拒绝的函数的主体。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-04
        • 2020-03-25
        • 1970-01-01
        • 1970-01-01
        • 2013-11-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多