Coq最佳实践:相互递归,只有一个函数在结构上减少

时间:2017-06-28 02:42:46

标签: coq mutual-recursion totality

考虑以下无类型lambda演算的玩具表示:

Require Import String.
Open Scope string_scope.

Inductive term : Set :=
| Var : string -> term
| Abs : string -> term -> term
| App : term -> term -> term.

Fixpoint print (term : term) :=
  match term return string with
  | Var id => id
  | Abs id term => "\" ++ id ++ " " ++ print term
  | App term1 term2 => print_inner term1 ++ " " ++ print_inner term2
  end
with print_inner (term : term) :=
  match term return string with
  | Var id => id
  | term => "(" ++ print term ++ ")"
  end.

类型检查print失败,并显示以下错误:

Recursive definition of print_inner is ill-formed.
[...]
Recursive call to print has principal argument equal to "term" instead of "t".

实现这一目标的最具可读性/符合人体工程学/效率的方法是什么?

2 个答案:

答案 0 :(得分:2)

您可以使用嵌套递归函数:

Fixpoint print (tm : term) : string :=
  match tm return string with
  | Var id => id
  | Abs id body => "\" ++ id ++ ". " ++ print body
  | App tm1 tm2 =>
     let fix print_inner (tm : term) : string :=
         match tm return string with
         | Var id => id
         | _ => "(" ++ print tm ++ ")"
         end
     in
     print_inner tm1 ++ " " ++ print_inner tm2
  end.

这种方法可以扩展到处理漂亮打印 - 通常的惯例是不要在x y z等表达式中打印括号(左边的应用程序关联)或打印\x. \y. x y {{1} }:

\xy. x y

顺便说一句,CPDT在相互递归与嵌套递归上有some material,但是在不同的设置中。

答案 1 :(得分:2)

您也可以通过print_inner执行的案例分析来解除递归调用的想法,如下所示:

Definition print_inner (term : term) (sterm : string) : string :=
 match term with
 | Var id => id
 | _      => "(" ++ sterm ++ ")"
 end.

Fixpoint print (term : term) :=
  match term return string with
  | Var id => id
  | Abs id term => "\" ++ id ++ " " ++ print term
  | App term1 term2 => print_inner term1 (print term1)
                    ++ " " ++ print_inner term2 (print term2)
  end.

或者,您可以使用不同的算法,依赖于构造函数的固定级别来决定是否忽略括号。