归纳类型和 nat 的相互递归

mutual recursion on an inductive type and nat

考虑这个例子:

Inductive T :=
| foo : T
| bar : nat -> T -> T.

Fixpoint evalT (t:T) {struct t} : nat :=
  match t with
  | foo => 1
  | bar n x => evalBar x n
  end
with evalBar (x:T) (n:nat) {struct n} : nat :=
  match n with
  | O => 0
  | S n' => (evalT x) + (evalBar x n')
  end.

Coq 拒绝它并出现错误:递归调用 evalBar 的主要参数等于 "n" 而不是 "x"

我知道终止检查器被两个不相关的归纳类型(T 和 nat)弄糊涂了。但是,看起来我试图定义的函数确实会终止。我怎样才能让 Coq 接受它?

我找到的一个解决方案是使用 nat_rec 而不是 evalBar:

Fixpoint evalT (t:T) {struct t} : nat :=
  match t with
  | foo => 1
  | bar n x => @nat_rec _ 0 (fun n' t' => (evalT x) + t') n
  end.

它有效,但我希望我可以将 nat_rec 隐藏在 evalBar 定义下以隐藏详细信息。在我的实际项目中,这种构造被多次使用。

另一种解决方案是使用嵌套固定点。

Fixpoint evalT (t:T) {struct t} : nat :=
  match t with
  | foo => 1
  | bar n x => let fix evalBar n {struct n} :=
                 match n with
                 | 0 => 0
                 | S n' => Nat.add (evalT x) (evalBar n')
                 end
               in evalBar n
  end.

重点是从 evalBar 中删除参数 x。因此,对 evalT 的递归调用是在 bar n xx 上完成的,而不是作为 evalBar 的参数给出的 x,因此终止检查器可以验证 evalT.

的定义

这与在另一个答案中提出的 nat_rec 版本的想法相同。