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

发布于 2024-08-13 05:28:29 字数 1145 浏览 4 评论 0原文

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

I'm writing code that deals with values from Language.Exts.Annotated.Syntax, where a variety of types are defined that mirror the structure of a Haskell module:

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

I'd like to be able to write functions that walk these data structures and perform various transformations on them. Because there's no one common data type, I can't write one function that does everything.

So far I've written a Tree type that wraps each of these types so that my transformation function can do Tree l -> Tree l:

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

However I'm now finding myself writing a lot of code that takes a Module, wraps it ModuleT, calls a function, then unwraps the result back to Module again. I have:

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

Two questions:

  1. Given that I can't change the types in Language.Exts.Annotated.Syntax, am I going about this the wrong way?
  2. If not, can I cut down on all this boilerplate somehow?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

瘫痪情歌 2024-08-20 05:28:29

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

我个人最喜欢的是单板。例如,从 Tree 收集所有 GuardedAlt 非常简单:

import Data.Uniplate.PlateData

...

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

您可以查看我的包 graphtype< /a> 我在那里做了类似的事情。

All of those types seem to be instances of Typeable and Data. You can define your type Tree to be an instance of Typeable and Data as well, and then use one of the available generics libraries (SYB, uniplate, ...) to traverse the Tree with ease.

My personal favorite is uniplate. For example, collecting all GuardedAlt from Tree would be as easy as:

import Data.Uniplate.PlateData

...

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

You could take a look at my package graphtype where I did similar things.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文