Haskell 在处理许多不相关的类型时避免使用样板文件

Haskell 在处理许多不相关的类型时避免使用样板文件,haskell,generic-programming,Haskell,Generic Programming,我正在编写处理来自的值的代码,其中定义了反映Haskell模块结构的各种类型: data Module l = ... data Decl l = ... data Exp t = ... -- etc 我希望能够编写遍历这些数据结构并对其执行各种转换的函数。因为没有一种通用的数据类型,所以我不能编写一个函数来完成所有工作 到目前为止,我已经编写了一个树类型,它包装了这些类型中的每一个,以便我的转换函数可以执行树l->树l: data Tree l = ModuleT (Module l)

我正在编写处理来自的值的代码,其中定义了反映Haskell模块结构的各种类型:

data Module l = ...
data Decl l = ...
data Exp t = ...
-- etc
我希望能够编写遍历这些数据结构并对其执行各种转换的函数。因为没有一种通用的数据类型,所以我不能编写一个函数来完成所有工作

到目前为止,我已经编写了一个
类型,它包装了这些类型中的每一个,以便我的转换函数可以执行
树l->树l

data Tree l = ModuleT (Module l)
            | DeclT (Decl l)
            | ExpT (Exp l)
            -- etc copy & paste
然而,我现在发现自己编写了很多代码,这些代码需要一个
模块
,将其包装成
模块
,调用一个函数,然后再次将结果解包回
模块
。我有:

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
两个问题:

  • 考虑到我无法更改Language.Exts.Annotated.Syntax中的类型,我这样做是错误的吗
  • 如果不是的话,我能以某种方式减少所有这些样板文件吗

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

    我个人最喜欢的是uniplate。例如,从树中收集所有GuardeAlt非常简单:

    import Data.Uniplate.PlateData
    
    ...
    
    allGuardedAlts :: Tree l -> [l]
    allGuardedAlts t = [ l | GuardedAlt l _ _ <- universeBi t]
    
    import Data.Uniplate.PlateData
    ...
    AllGuardAlts::树l->[l]
    
    所有防护等级t=[l | GuardedAlt l uuuuuuUniplate看起来正是我所需要的,谢谢。我现在已经废弃了我的样板。通过这种方法,你不再真正需要你的树类型了,是吗?没错,我不需要;我可以将我的树重写函数设置为一系列重写BI应用程序,我关心的每种类型一个。这现在提出了一个后续问题:我似乎不能保持“l”的通用性-