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

标签 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".


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

最佳答案

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

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打印为\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在相互递归与嵌套递归上具有some material,但是设置不同。

关于coq - Coq最佳实践:相互递归,只有一个函数在结构上递减,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44792791/

相关文章:

coq - 如何在 Coq 中编写以下形式的函数?

Coq:为什么我需要手动展开一个值,即使它上面有 `Hint Unfold`?

coq - coq 中自然数的欧几里德划分

recursion - F#:互递归数据结构的变态

haskell - Haskell相互递归的澄清

javascript - 如何将多元定点组合器翻译成严格的语言?

coq - 找到一个成立良好的关系来证明函数在某个点停止减少的终止

coq - Coq 中推/弹出计算器导致的奇怪的证明义务

types - Coq:显示环境中一个或多个类型中的所有术语

idris - 为什么这个 'with' block 会破坏这个函数的完整性?