现在,我已经有了一个AST表达式,它在递归类型上是多态的:
data Expr a = Const Int
| Add a a
这非常有用,它允许我使用一种类型进行普通递归(Fix Expr
),而当我需要附加其他信息时使用另一种类型(Cofree Expr ann
)。
当我想在此递归方案中引入另一种类型时,就会出现问题:
data Stmt a = Compound [a]
| Print (Expr ?)
我不确定在不引入其他类型变量和破坏与我已经编写的所有常规功能的兼容性的情况下,Expr
术语中的含义。
可以这样做吗?如果可以,这是有用的模式吗?
答案 0 :(得分:4)
递归方案的观点是将递归类型视为函子的固定点。表达式的类型是以下函子的固定点:
data ExprF expr = Const Int
| Add expr expr
更改变量名称的目的是要明确表明它是表达式的实际类型的占位符,否则将其定义为:
data Expr = Const Int | Add Expr Expr
在Stmt
中,有两种递归类型,Expr
和Stmt
本身。因此,我们提出了两个漏洞/未知数。
data StmtF expr stmt = Compound [stmt]
| Print expr
当我们使用Fix
或Cofree
作为定点时,我们现在正在解决一个由两个方程组成的系统(一个用于Expr
,一个用于Stmt
),带有一些样板。
我们没有直接应用Fix
或Cofree
,而是进行了概括,将那些定点组合器(Fix
,Cofree
,Free
)作为构造中的参数表达式和语句:
type Expr_ f = f ExprF
type Stmt_ f = f (StmtF (Expr_ f))
现在,对于未注释的树,我们可以说Expr_ Fix
或Stmt_ Fix
,对于Expr_ (Flip Cofree ann)
,Stmt_ (Flip Cofree ann)
来说,我们可以说。不幸的是,我们必须另外支付LOC费用才能使种类匹配,并且种类变得更加复杂。
newtype Flip cofree a f b = Flip (cofree f a b)
(这还假设我们想同时在各处使用相同的Fix
或Cofree
。)
要考虑的另一种表示形式是(called HKD nowadays):
data Expr f = Const Int
| Add (f Expr) (f Expr)
data Stmt f = Compount [f Stmt]
| Print (f (Expr f))
仅从批注/无批注(f = Identity
或(,) ann
)中提取,而不从递归中提取。