我刚刚进入我的第一个真正的Haskell大小项目(一个Web应用程序),我开始遇到来自第三方库的类型泄漏我的代码的问题.这是一个简单的例子:
我的Parser模块导入Test.Parsec,并导出一个parseConfig返回的函数()Either ParseError DbConfig,其中ParseError是Parsec库中DbConfig定义的数据类型(是我的应用程序的自定义数据类型,为简洁起见未显示).
-- Parser.hs
module Parser where
import Text.Parsec
parseConfig :: String -> Either ParseError DbConfig
parseConfig = parse ...
Run Code Online (Sandbox Code Playgroud)
后来,我想使用我的parseConfig功能,但为了使用它,我必须Text.Parsec再次导入,以便我可以访问该ParseError类型.
-- Api.hs
module Api where
import Parser
import Text.Parsec
getConfigFromBody :: Object -> Either ParseError DbConfig
getConfigFromBody = parseConfig . (...)
Run Code Online (Sandbox Code Playgroud)
这不仅是管理进口的麻烦,而且是关注点的差异分离,因此我知道这不是最好的方法.我的问题是,管理这个问题的最佳做法是什么?制作类型同义词是理想的吗?
type ConfigParseError = ParseError
parseConfig :: String -> Either ConfigParseError DbConfig
parseConfig = parse ...
Run Code Online (Sandbox Code Playgroud)
这似乎是合理的,因为保持Parsec我的Parser模块内部的依赖,但别名库类型似乎是一个默认使用的奇怪模式.
所以我的问题是,较大的Haskell应用程序或库如何处理这个问题?是否有从第三方库管理数据类型的通用技术?
我还没准备好提供一份很好的最佳实践列表,但对于初学者来说,如果你想要保持井井有条的组织,请使用显式导出而不是仅导出所有内容,例如:
module Parser
( parseConfig
) where
...
Run Code Online (Sandbox Code Playgroud)
显式导出还允许您重新导出导入,例如
module Parser
( parseConfig
, ParseError(..)
) where
...
Run Code Online (Sandbox Code Playgroud)
在那之后你可以import Parser,并且ParseError可以像在里面定义一样Parser.
我认为这应该可以解决您当前的问题.