我发现自己一遍又一遍地重复着一个模式,我想将其抽象化。我相当有信心,coq足够表达这种模式,但我在弄清如何做到这一点时遇到了一些困难。我正在定义一种编程语言,其中包含互递归的归纳数据类型,表示语法术语:
Inductive Expr : Set :=
| eLambda (x:TermVar) (e:Expr)
| eVar (x:TermVar)
| eAscribe (e:Expr) (t:IFType)
| ePlus (e1:Expr) (e2:Expr)
| ... many other forms ...
with DType : Set :=
| tArrow (x:TermVar) (t:DType) (c:Constraint) (t':DType)
| tInt
| ... many other forms ...
with Constraint : Set :=
| cEq (e1:Expr) (e2:Expr)
| ...
现在,我需要定义一些与这些类型相关的函数。例如,我想要一个函数来查找所有自由变量,一个用于执行替换的函数以及一个提取所有约束集合的函数。这些函数都具有以下形式:
Fixpoint doExpr (e:Expr) := match e with
(* one or two Interesting cases *)
| ...
(* lots and lots of boring cases,
** all of which just recurse on the subterms
** and then combine the results in the same way
*)
| ....
with doIFType (t:IFType) := match t with
(* same structure as above *)
with doConstraint (c:Constraint) := match c with
(* ditto *)
例如,为了查找自由变量,我需要在变量情况和绑定情况下进行一些有趣的操作,但对于其他所有情况,我只需递归地查找子表达式的所有自由变量,然后将这些列表合并在一起。对于生成所有约束条件列表的函数也是如此。替换情况有点棘手,因为三个函数的结果类型不同,并且用于组合子表达式的构造函数也不同:
Variable x:TermVar, v:Expr.
Fixpoint substInExpr (e:Expr) : **Expr** := match e with
(* interesting cases *)
| eLambda y e' =>
if x = y then eLambda y e' else eLambda y (substInExpr e')
| eVar y =>
if x = y then v else y
(* boring cases *)
| eAscribe e' t => **eAscribe** (substInExpr e') (substInType t)
| ePlus e1 e2 => **ePlus** (substInExpr e1) (substInExpr e2)
| ...
with substInType (t:Type) : **Type** := match t with ...
with substInConstraint (c:Constraint) : **Constraint** := ...
.
写这些函数是乏味和容易出错的,因为我必须为每个函数写出所有无趣的情况,并确保对所有子项进行递归。 我想要编写的是以下类似的内容:
Fixpoint freeVars X:syntax := match X with
| syntaxExpr eVar x => [x]
| syntaxExpr eLambda x e => remove x (freeVars e)
| syntaxType tArrow x t1 c t2 => remove x (freeVars t1)++(freeVars c)++(freeVars t2)
| _ _ args => fold (++) (map freeVars args)
end.
Variable x:TermVar, v:Expr.
Fixpoint subst X:syntax := match X with
| syntaxExpr eVar y => if y = x then v else eVar y
| syntaxExpr eLambda y e => eLambda y (if y = x then e else (subst e))
| syntaxType tArrow ...
| _ cons args => cons (map subst args)
end.
这个想法的关键在于能够将构造函数应用于若干参数,并且有一种“映射”能够保留参数的类型和数量。
显然,这个伪代码并不能正常工作,因为_的情况是不正确的。所以我的问题是,是否有可能编写这种组织方式的代码,或者我只能手动列出所有无聊的情况?