Haskell模块命名规范

37
我应该如何为我的Haskell程序命名模块,在层次结构中组织它们?(这是一个针对程序而非库的问题)
我正在制作一个名为Luminosity的光线追踪器。起初,我有以下这些模块:
Vector Colour Intersect Trace Render Parse Export

每个模块本身都很好,但我觉得缺乏组织。
首先,我把每个模块放在“Luminosity”下,所以例如“Vector”现在是“Luminosity.Vector”(我认为这是Haskell程序的标准格式?)。
然后我想:向量和颜色是独立的,可以重复使用,因此它们应该分开。但它们太小了,无法成为库。
它们应该放在哪里?已经有了一个Data.VectorData.Colour(在hackage上),所以我应该把它们放在那里吗?或者会引起混淆(即使我将它们与其他本地导入一起分组导入)?如果不是那里,应该是Luminosity.Data.Vector还是Data.Luminosity.Vector?我很确定我看到过两种格式的使用,尽管可能只是看到了使用非传统结构的项目。
我还有一个简单的TGA图像导出器(Export),它可以独立于Luminosity。它似乎应该被放在Codec.Image.TGA中,但同样,Luminosity是否应该出现在其中,如果是,应该放在哪里?

如果Haskell项目的结构或其他维基能够解释这个问题,那就太好了。


2
如果你想要制作可重用的代码片段,就将它打包成一个库。大小并不重要。 - Cat Plus Plus
2
可重用的几何基元模块——具有代数类型,向量和颜色定义起来非常容易,以至于对于严肃的使用者,Haskeller 都想要定义自己的模块,而不是依赖于其他库。这样他们可以控制自己的表示,并且不必担心依赖问题(API 更改、作者失踪等)。 - stephen tetley
2个回答

22
除非你的程序真的很大,否则不要将模块组织成层次结构。为什么呢?因为尽管计算机擅长层次结构,但人类并不擅长。人类善于使用有意义的名称。如果你选择好名称,可以在一个平面命名空间中轻松处理150个模块。
“我觉得[平面命名空间]缺乏组织。”
层次结构组织本身并不是一个目标。为了将模块分解为层次结构,你需要一个理由。好的理由往往与信息隐藏或重用有关。当你引入信息隐藏时,你已经走了库设计的一半路程,而当你谈论重用时,你实际上正在构建一个库。将一个大程序变成“较小程序加上库”是软件演进的一个好策略,但看起来你刚刚开始,你的程序还没有足够大以这种方式演进。

这些问题与您所使用的编程语言基本无关。我建议阅读David Parnas关于产品线和程序族的一些研究成果,以及Matthias Blume鲜为人知的论文分层模块化。这些作品将为您提供更具体的想法,了解何时层次结构开始有用。


1
这是一个有趣的观点!我通常在我的程序中至少有某种形式的分层组织。你认为“真正大”的阈值是多少数量级? - Chris Taylor
我有点理解你的观点,但我不同意这个想法与编程语言无关。至少为了避免命名冲突,应该引入一级(Luminosity.*)——虽然我不确定你是否将其视为层次结构。我并不是在问层次结构的利弊,我特别询问的是Haskell的约定。也许这是无谓的争论,但是Haskell base 层次结构(以及Hackage上的包的层次结构)似乎比Java或Python的约定要复杂得多且不那么简单。 - mk12
关于平面结构和层次结构的讨论,我为我的8个模块程序制作层次结构的原因是:大约一半的模块与光线追踪无关。我可以在本地文件浏览器中适当地组织它们,但在GitHub上它们都按字母顺序排列。我认为通过将相关模块分组,人们更容易理解整个程序。这似乎与你所争辩的完全相反。 - mk12
@ChrisTaylor:按照我的标准,我从未参与过真正大型的项目。但是对于代码行数在10,000到50,000之间、模块数量在80到150之间的项目,扁平化结构对我来说效果很好。也许100,000行的代码才算是真正的大项目? - Norman Ramsey
2
@Mk12:这很快就变成了讨论,而不是问答,但我更好地理解了你的冲动。许多项目都有一个“垃圾箱”,可以把有用但不直接相关的东西扔进去。这些东西是模糊的通用性,但又不够大或优秀,不能转化为库。我通常称我的垃圾箱为Util。至于base层次结构,它明确是,而不是程序,所以适用不同的规则。 - Norman Ramsey

15
首先,我将每个模块都放在了 Luminosity 下面。 我认为这是一个很好的举措。它可以让阅读代码的任何人明确知道这些模块是专门为 Luminosity 项目而创建的。
如果你编写一个模块,其意图是模拟或改进现有库,或填补你认为特定通用库缺失的空白,那么在这种罕见情况下,请省略前缀并以通用方式命名。例如,查看 pipes 包如何导出 Control.Monad.Trans.Free,因为作者对 Free monads 的现有实现不满意。
然后我想,向量和颜色基本上是独立的,可以被重复使用,所以它们应该分开。但它们太小了,无法分离成一个独立的库(分别只有125和42行)。那它们应该放在哪里呢?
如果您不制作单独的库,则可能将它们保留在 Luminosity.VectorLuminosity.Colour 中。如果您确实要制作单独的库,请尝试给那些库的目标受众发送电子邮件,了解其他人认为这些库应该如何命名和分类。无论您是否将其拆分为单独的库完全取决于您以及您认为这些单独的库对其他人可能提供多少好处。

我接受了你的答案,因为我认为它对我最有帮助并且很具体,但我结合了你、Norman和Stephen(在问题上的评论)的建议。像你说的那样,我会把所有东西都放在 Luminosity. 下面。我不会费心将我的小模块制作成库或删除前缀,因为正如Stephen所说,每个程序可能都想要自己的向量和颜色。出于Norman提出的原因,我会保持一切扁平化,而不是在 Luminosity. 下引入层次结构。 - mk12

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接