对不起我非常有限的Haskell-fu。
我有一系列data
类型,在不同的模块中定义,结构相同:
-- in module Foo
data Foo = Foo [Param]
-- in module Bar
data Bar = Bar [Param]
-- * many more elsewhere
我希望有一组在params列表上运行的函数,例如在列表中添加和删除元素(返回一个新的Foo
或Bar
,其中包含不同的列表params,视情况而定)。
据我所知,即使我创建一个类型类并为每种类型创建实例,我也需要每次都定义所有这些函数,即:
-- in some imported module
class Parameterized a where
addParam :: a -> Param -> a
-- ... other functions
-- in module Foo
instance Parameterization Foo where
addParam (Foo params) param = Foo (param:params)
-- ... other functions
-- in module Bar
instance Parameterization Bar where
-- this looks familiar...
addParam (Bar params) param = Bar (param:params)
-- ... other functions
这感觉很乏味 - 远远超过了我开始认为我做错了什么的程度。如果无法使用构造函数(?)进行模式匹配来提取值,那么如何减少这样的样板?
要反驳一个可能的参数:是的,我知道我可以简单地拥有一组函数(addParam
等),它们会明确地列出每个构造函数和模式匹配的参数 - 但是就像我一样我相当模块化地构建这个模块(Foo
和Bar
模块是非常独立的),我正在构建一个系统,其中将存在数十种类型,类型构造函数的详细集中列表似乎......错了。
很可能(很可能?)我的方法只是有缺陷,而且无论如何都不能以正确的方式构建类型层次结构 - 但因为我不能有一个data
类型在某个地方并为每个模块中的类型添加一个新的构造函数(?)我很难理解如何获得一个漂亮的“插件”感觉,而不必每次都重新定义简单的实用程序函数。任何和所有善意的建议都很乐意接受。
答案 0 :(得分:4)
您可以在类型类中使用函数的默认实现,例如
class Parameterized a where
params :: a -> [Param]
fromParams :: [Param] -> a
addParam :: a -> Param -> a
addParam x par = fromParams $ par : params x
-- ... other functions
instance Parameterized Foo where
params (Foo pars) = pars
fromParams = Foo
但是,您的设计确实看起来很可疑。
答案 1 :(得分:3)
您可以为Foo和Bar使用newtype,然后使用-XGenerializedNewtypeDeriving
自动基于底层结构(在本例中为列表)派生实现。
如果他们真的应该在结构上相似,但你已经以不能共享代码的方式对其进行编码,那么这就是一种可疑模式。
答案 2 :(得分:2)
从你的例子中很难说出正确的方法。
如果结构相似,为什么同时需要Foo
和Bar
?你不能跟Foo
一起去吗?
如果由于某些原因我看不到,你需要Foo
和Bar
,你必须使用类型类,但你可以使用Template Haskell清理代码。
像
这样的东西$(superDuper "Foo")
可以生成代码
data Foo = Foo [Param]
instance Parameterization Foo where
addParam (Foo params) param = Foo (param:params)
,其中
superDuper :: String -> Q [Dec]
superDuper n =
let name = mkName n
dataD = DataD [] name [] [NormalC name [] ] -- correct constructor here
instD = InstanceD [] ... -- add code here
return [dataD, instD]
那至少可以摆脱锅炉板编码。