【问题标题】:Avoiding boilerplate when dealing with many unrelated types在处理许多不相关的类型时避免样板
【发布时间】:2009-12-06 13:17:24
【问题描述】:

我正在编写处理来自 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. 如果没有,我可以以某种方式减少所有这些样板吗?

【问题讨论】:

    标签: haskell generic-programming


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 单板看起来正是我所需要的,谢谢。我现在已经废弃了我的样板。
    • 通过这种方法,您不再真的需要 Tree 类型了,是吗?
    • 没错,我没有;我可以将我的树重写函数设置为一系列 rewriteBi 应用程序,每个我关心的类型都有一个。
    • 这引发了一个后续问题:我似乎无法保持 'l' 通用 - stackoverflow.com/questions/1865351/ambiguous-type-variable
    猜你喜欢
    • 2021-02-28
    • 1970-01-01
    • 1970-01-01
    • 2011-04-14
    • 2013-01-25
    • 1970-01-01
    • 1970-01-01
    • 2016-05-27
    • 1970-01-01
    相关资源
    最近更新 更多