Coq 终止检查器何时减少记录访问器

Coq 终止检查器何时减少记录访问器,coq,termination,Coq,Termination,我对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) := {|

我对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能够解析记录访问器,基本上将我们在
树中的内容P1
与在
树中的内容P2
进行比较

但这似乎只适用于在本地构建记录的情况(
let tree\u C:=…
),而不适用于使用
Definition
定义的情况


但是
Fixpoint
可以很好地查看其他定义,例如通过
list\p
。那么,记录有什么特别之处,我可以让Coq接受问题1的
tree\u P3

。我相信在
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.
在此定义中,注释
(*标记*)
后所需的类实例由第7行的定义填充。但此定义不在
修复
构造中,终止检查器不会以相同的方式减少此定义。因此,未应用于任何树参数的
tree\u P1'
事件将保留在代码中,并且终止检查器将无法确定此事件仅用于小于初始参数的参数


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

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

终止检查器将始终展开本地定义,并减少。这就是
树P1
工作的原因

如有必要,终止检查器还将展开所调用的定义(如
list\u C'
p
existsb
),这就是
tree\u P2
工作的原因

但是,终止检查器将不会展开
中与
子句匹配的定义,例如
列表_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.