有没有更好的方法可以将属性字段添加到Haskell的AST中?

时间:2019-07-09 10:50:22

标签: haskell

首先,我有一个原始的AST定义,如下所示:

data Expr = LitI Int | LitB Bool | Add Expr Expr

我想对此进行概括,以便每个AST节点都可以包含一些额外的属性:

data Expr a = LitI Int a | LitB Bool a | Add (Expr a) (Expr a) a

通过这种方式,我们可以轻松地将属性附加到AST的每个节点中:

type ExprWithType = Expr TypeRep
type ExprWithSize = Expr Int

但是这种解决方案使访问属性字段变得困难,我们必须使用模式匹配并逐个处理它:

attribute :: Expr a -> a
attribute e = case e of
    LitI _ a -> a
    LitB _ a -> a
    Add _ _ a -> a

如果可以通过原始AST的产品类型和指示属性的类型变量来定义AST,我们可以想象得到

type ExprWithType = (Expr, TypeRep)
type ExprWithSize = (Expr, Int)

然后我们可以简化属性访问函数,如下所示:

attribute = snd

但是我们知道,最外面的产品类型的属性不会递归地出现在子树中。

那么,有没有更好的解决方案?

通常来说,当我们要提取递归和类型的不同情况的公共字段时,我们遇到了这个问题。

2 个答案:

答案 0 :(得分:2)

您可以“提升” Expr的类型,例如:

data Expr e = LitI Int | LitB Bool | Add e e

现在我们可以定义一种数据类型,

data ExprAttr a = ExprAttr {
    expression :: Expr (ExprAttr a),
    attribute :: a
  }

因此,这里的ExprAttr有两个参数,expression,这是一个Expr会话,在树中有ExprAttr a个,而attribute这是a

因此,您可以处理ExprAttr秒的AST。如果要使用“简单” AST,则可以定义以下类型:

ExprAttr

答案 1 :(得分:1)

您可能想看看Cofree,其中f将是您的递归数据类型,将递归的概念抽象为f-algebra,而a将是递归数据类型。注释的类型。

Nate Faubion对这种方法和类似方法进行了非常深入的讨论,您可以在这里观看:https://www.youtube.com/watch?v=eKkxmVFcd74