在处理许多不相关的类型时避免样板

时间:2009-12-06 13:17:25

标签: haskell generic-programming

我正在编写处理来自Language.Exts.Annotated.Syntax的值的代码,其中定义了各种类型的镜像Haskell模块的结构:

data Module l = ...
data Decl l = ...
data Exp t = ...
-- etc

我希望能够编写遍历这些数据结构并对它们执行各种转换的函数。因为没有一种常见的数据类型,所以我不能编写一个能完成所有工作的函数。

到目前为止,我已经写了一个Tree类型来包装这些类型,以便我的转换函数可以Tree l -> Tree l

data Tree l = ModuleT (Module l)
            | DeclT (Decl l)
            | ExpT (Exp l)
            -- etc copy & paste

但是我现在发现自己编写了大量代码,需要Module,将其包装ModuleT,调用函数,然后再将结果解包回Module。我有:

class AnnotatedTree ast where
  tree :: ast l -> Tree l
  untree :: Tree l -> ast l

instance AnnotatedTree Module where
  tree = ModuleT
  untree (ModuleT x) = x
  untree _ = error "expected ModuleT"

-- etc ad nauseam

两个问题:

  1. 鉴于我无法更改Language.Exts.Annotated.Syntax中的类型,我是否会以错误的方式进行此操作?
  2. 如果没有,我能否以某种方式减少所有这些样板?

1 个答案:

答案 0 :(得分:6)

所有这些类型似乎都是Typeable和Data的实例。您可以将类型Tree定义为Typeable和Data的实例,然后使用一个可用的泛型库(SYB,uniplate,...)轻松遍历树。

我个人最喜欢的是uniplate。例如,从Tree收集所有GuardedAlt就像这样简单:

import Data.Uniplate.PlateData

...

allGuardedAlts :: Tree l -> [l]
allGuardedAlts t = [ l | GuardedAlt l _ _ <- universeBi t]

你可以看看我的包graphtype,我做了类似的事情。