处理许多不相关的类型时避免样板
我正在编写处理 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
两个问题:
- 鉴于我无法更改 Language.Exts.Annotated.Syntax 中的类型,我是否以错误的方式处理此问题?
- 如果没有,我可以以某种方式减少所有这些样板吗?
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:
- Given that I can't change the types in Language.Exts.Annotated.Syntax, am I going about this the wrong way?
- If not, can I cut down on all this boilerplate somehow?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
所有这些类型似乎都是 Typeable 和 Data 的实例。您也可以将类型树定义为 Typeable 和 Data 的实例,然后使用可用的泛型库之一(SYB、uniplate...)轻松遍历树。
我个人最喜欢的是单板。例如,从 Tree 收集所有 GuardedAlt 非常简单:
您可以查看我的包 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:
You could take a look at my package graphtype where I did similar things.