代码之家  ›  专栏  ›  技术社区  ›  Tim Robinson

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

  •  9
  • Tim Robinson  · 技术社区  · 15 年前

    我正在编写处理来自 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 ,调用一个函数,然后将结果展开回 模块 再一次。我有:

    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 回复  |  直到 15 年前
        1
  •  6
  •   ADEpt    15 年前

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

    我个人最喜欢的是Uniplate。例如,从树上收集所有的保护物就像:

    import Data.Uniplate.PlateData
    
    ...
    
    allGuardedAlts :: Tree l -> [l]
    allGuardedAlts t = [ l | GuardedAlt l _ _ <- universeBi t]
    

    你可以看看我的包裹 graphtype 我做过类似的事情。