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

Coq最佳实践:相互递归,只有一个函数在结构上是递减的,coq,mutual-recursion,totality,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 str

考虑以下非类型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.
类型检查
打印
失败,出现以下错误:

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

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

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

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.
这种方法可以扩展到处理漂亮的打印——通常的惯例是不打印表达式中的括号,如
xyz
(应用程序关联到左侧)或打印
\x\Yx y
\xy。x y

Definition in_parens (stm : string) : string := "(" ++ stm ++ ")".

Fixpoint pprint (tm : term) : string :=
  match tm with
  | Var id => id
  | Abs id tm1 =>
    let fix pprint_nested_abs (tm : term) : string :=
        match tm with
        | Abs id tm1 => id ++ pprint_nested_abs tm1
        | _ => ". " ++ pprint tm
        end
    in
    "\" ++ id ++ pprint_nested_abs tm1

  (* e.g. (\x. x x) (\x. x x) *)
  | App ((Abs _ _) as tm1) ((Abs _ _) as tm2) =>     
      in_parens (pprint tm1) ++ " " ++ in_parens (pprint tm2)

  (* variable scopes *)
  | App ((Abs _ _) as tm1) tm2 => in_parens (pprint tm1) ++ " " ++ pprint tm2

  (* `x \x. x` looks ugly, `x (\x. x)` is better; also handle `x (y z)` *) 
  | App tm1 ((Abs _ _) as tm2) | App tm1 (App _ _ as tm2) =>
      pprint tm1 ++ " " ++ in_parens (pprint tm2)

  | App tm1 tm2 => pprint tm1 ++ " " ++ pprint tm2
  end.

顺便说一句,CPDT对相互递归和嵌套递归进行了比较,但设置不同。

您还可以将递归调用的想法与由
print\u 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.

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

“或者,您可以使用依赖于构造函数的固定性级别的不同算法来决定是否删除括号。”你有没有读过关于那可能是什么样子的书?@CarlPatenaudePoulin,比如: