在Haskell中组织公共类型:多同名Common.hs文件的困扰
这个问题在Haskell项目里太常见了——当你开始扩展模块时,同名的Common.hs很容易造成混淆。我给你几个实用的解决思路,你可以根据项目的规模和你的偏好来选:
给Common模块起更具针对性的名字
这是最直接也最推荐的做法。把Foo目录下的Common.hs改成FooTypes.hs或者FooCore.hs,对应的模块名就变成Foo.FooTypes;Bar目录下的改成BarTypes.hs,模块名Bar.BarTypes。这样导入的时候写import Foo.FooTypes,一眼就能看出来是哪个模块下的公共类型,完全避免了同名冲突,可读性也拉满。唯一的小成本就是改几个文件名和导入语句,长远来看绝对值得。使用导入别名区分
如果不想改动现有文件结构,那可以在导入的时候给模块加别名。比如:
import qualified Foo.Common as FooCommon -- 用FooCommon指代Foo下的Common import Bar.Common as BarCommon -- 用BarCommon指代Bar下的Common
之后使用类型的时候就写FooCommon.UserId或者BarCommon.OrderStatus,清晰区分。这个方案适合临时过渡或者小项目,不用改文件,但要注意别名起得直观,别用太模糊的缩写,不然时间长了自己都记混。
将Common模块放到内部子目录
可以给每个模块目录加个Internal子目录,比如把Foo/Common.hs移到Foo/Internal/Common.hs,模块名变成Foo.Internal.Common;Bar下的同理变成Bar.Internal.Common。这样导入的时候写import Foo.Internal.Common,既保留了Common这个名字,又通过层级明确了它是Foo模块的内部公共组件,不会和Bar的混淆。这个方式还能传递“这个模块是内部用的,不建议外部直接依赖”的信号,适合有明确内部/外部模块划分的项目。利用Cabal组件隔离(适合中大型项目)
如果你的项目用Cabal管理,可以把Foo和Bar拆成独立的library组件。在.cabal文件里分别定义:
library foo-lib exposed-modules: Foo.Foo1 Foo.Foo2 Foo.Common hs-source-dirs: src/Foo library bar-lib exposed-modules: Bar.Bar1 Bar.Bar2 Bar.Common hs-source-dirs: src/Bar
这样每个library的模块空间是隔离的,在foo-lib里可以直接写import Common(因为hs-source-dirs是src/Foo,模块名就是Common),bar-lib里同理。而当其他组件依赖它们时,需要写import qualified Foo.Common as FooCommon或者import Bar.Common,不会冲突。这个方案适合项目比较大,需要模块化管理的情况,能很好地隔离不同模块的内部依赖。
内容的提问来源于stack exchange,提问作者Petras Purlys

