在Haskell中以通用方式扩充复杂数据类型

时间:2015-11-23 10:58:34

标签: haskell generics data-structures types abstract-syntax-tree

我一直在使用Language.C library的抽象语法树(AST)来使用SYB library的泛型转换来修改C程序。这个AST有不同类型的节点(数据类型),每个节点代表一个C结构,即表达式,语句,定义等。我现在需要以某种方式增加语句携带的信息,即注释它们。我认为(也许我错了)我无法修改或重新定义原始数据类型,所以我希望有这样的东西:

annotateAST anns =
   everywhere (mkT (annotateAST_ anns))

annotateAST_ astnode anns 
  | isStmt astnode = AnnStmt astnode (getAnn astnode anns)
  | otherwise = astnode

通过这种方式,我会使用带注释的语句而不是原始语句。当然,GHC会抱怨,因为everywhere应该返回它所获得的相同类型,而这不是它在这里发生的事情。

总而言之,我需要在不修改原始数据类型的情况下通常注释AST,并且很容易返回到原始数据结构。 我一直在为这个问题考虑不同的解决方案,但不相信任何一个,所以我决定在这里分享。

P.S。我被告知SYB库效率不高。考虑到Language.C的AST只导出数据,我是否有更有效的替代方法来进行AST的泛型遍历和修改?

1 个答案:

答案 0 :(得分:3)

我不是该库的专家,但似乎是为了允许用户定义的装饰而设计的。

这是因为所有主要数据类型都是NodeInfo的参数化,标准注释(仅携带位置和名称信息)。例如。图书馆提供

type CTranslUnit = CTranslationUnit NodeInfo

允许您定义

type MyTransUnit = CTranslationUnit MyNodeInfo
data MyNodeInfo = MNI NodeInfo AdditionalStuffHere

所以要按照你的意愿装饰AST。

库提供可影响此类装饰的Functor个实例,以及Annotated类型类从任何AST节点检索(可能是用户定义的)注释。

我会尝试采用这种方法。

设计看起来不错。我可以看到的唯一缺点是注释类型对于节点上的所有类型必须是相同的,这基本上迫使人们将其定义为内部可能具有的各种注释的巨大总和。例如:

-- AST library for a simple lambda-calculus
data AST n
    = Fun n String (AST n)
    | Var n String
    | App n (AST n) (AST n)

-- user code
data Annotation
    = AnnVar ... | AnnFun ... | AnnApp ...
type AnnotatedAST = AST Annotation

我们不会对仅使用AnnFun修饰的函数提供静态保证。

人们可能希望有更高级的图书馆设计来利用GADT,例如:

-- AST library for a simple lambda-calculus
data Tag = TagFun | TagVar | TagApp
data AST (n :: Tag -> *)
    = Fun (n 'TagFun) String (AST n)
    | Var (n 'TagVar) String
    | App (n 'TagApp) (AST n) (AST n)

-- user code
data Annotation (n :: Tag) where
   AnnFun :: String -> Annotation 'TagFun
   AnnVar :: Int    -> Annotation 'TagVar
   AnnApp :: Bool   -> Annotation 'TagApp
type AnnotatedAST = AST Annotation

保证每个节点都有正确的注释。 AST不再是Functor,但至少可以定义类似Functor的类。

仍然 - 我很感激至少该库允许某种形式的用户定义注释。