假设我有一个应用程序,其核心数据是项目,文档集(“DocSets”)和文档(“Docs”)。文档不限于任何特定的内容类型;它们可以是电子表格,图像,HTML等,并且可以具有其他文档不具有的功能/行为。同样,根据我需要的行为类型,我可能有多种DocSet。也许这个DocSet将文档呈现在一个平面列表中,也许是一个状态与每个文档相关联,并且根据状态有一些很酷的渲染技巧。
如果我没有使用类型类代码,代码可能看起来像这样:
data Project = Project {
projName :: Text,
projDocSets :: [DocSet]
}
data DocSet = DocSet {
dsName :: Text,
dsDocs :: [Doc]
}
data Doc = Doc {
docTitle :: Text,
docType :: Text,
docContents :: ByteString
}
我看到的问题是,虽然它允许有多种文档(通过docType字段),但它基本上使用手动类型检查,这感觉不对(在我脑海中)。我可以拥有PagedTextDoc,ContinuousTextDoc,SpreadsheetDoc,ImageDoc等的数据构造函数。这对我来说似乎很难模块化。
编辑:我的“模块化程度较差”评论依赖于未能很好传达的知识。每个Doc都有一些共同的行为 ((反)序列化,分组为DocSet,标题,渲染, 其他的应用程序演变),以及一些独特的行为 (内部表示以易于编辑的格式,节省增量 编辑,部分呈现为HTML,假设应用程序是一个Web应用程序)。
即使情况并非如此,在我看来应用程序也是如此 如果我可以创建一个新模块,定义我的文档更模块化 以及你可以用它做什么,现在支持bang,document。那 我在这里定义了我的分页文档,我的电子表格结束了 在那里,两个人根本不在乎彼此。
也许我可以使用Docs的类型类?
data DocSet = DocSet {
dsName :: Text,
dsDocs :: [Doc]
}
class Doc d where
docTitle :: d -> Text
docType :: d -> Text
docContents :: d -> ByteString
dsDocs的声明现在没有进行类型检查;列表仅适用于一种特定的具体类型,而不是类型类的任何成员。 DocSet肯定需要能够存储/引用多种文档。
我试图进行一些搜索,但坦率地说,我的Google技能完全失败了。我是否正确地考虑了数据结构?将所有(Proj,DocSet,Doc)作为单个数据构造函数的方法是否真的是处理此问题的最佳方法,还是我错过了Haskell处理多态的方式?
答案 0 :(得分:4)
那么,基于班级的提案甚至不起作用。类不是类型。如果[Doc]
是一个类,则您无法Doc
。
这里通常的方法是确定你正在建模的是什么。
你想要一堆表现一致的东西,并且可以很容易地打包在一起吗?然后将该行为放入类型中。这通常是通过创建一个记录来完成的,该记录包含您想要的每种行为的函数。
另一方面,你有一堆需要非常不同处理的东西吗?在这种情况下,将数据放入一堆类型中,让类型检查器(和穷举检查器)一路指导您,确保您始终在正确的位置处理正确的事情。
我建议不要使用课程。类(大多数)只适用于可以编写类型多态代码的情况,这些代码在具体类型时是有意义的,但您不需要知道类型是什么。 (这有点夸大其词,但它足够接近事实,将其用作第一近似启发式算法。)
答案 1 :(得分:2)
假设我有一个核心数据为Projects,Document的应用程序 设置(“DocSets”)和文档(“Docs”)。文档不限于 任何特定的内容类型;它们可以是电子表格,图像,HTML, 等等,可以有其他文件没有的功能/行为。
听起来你不知道类型可以有多个构造函数(求和类型)。而不是你的Doc
做:
data Doc = PagedTextDoc {docTitle :: Text, docContents :: ByteString}
| SpreadsheetDoc {docTitle :: Text, docContents :: ByteString}
... etc
同样,我可能有多种DocSet,具体取决于 我需要的行为类型。也许这个DocSet以一种方式呈现文档 平面列表,也许是一个与每个文档相关联的状态,以及 根据状态有一些很酷的渲染技巧。
data DocSet = ListDocset Text [Doc] | KoolDocset [(Status, Doc)] | ...etc