在Haskell中处理来自第三方库的数据类型的最佳实践?

在Haskell中处理来自第三方库的数据类型的最佳实践?,haskell,types,Haskell,Types,我刚刚进入我的第一个真正的Haskell项目(一个web应用程序),我开始遇到来自第三方库的类型泄漏到我的代码中的问题。下面是一个简单的例子: 我的Parser模块导入Test.Parsec,并导出一个函数(parseConfig),该函数返回ParseError DbConfig,其中ParseError是在Parsec库中定义的数据类型(DbConfig是我的应用程序的自定义数据类型,为简洁起见未显示) 稍后,我想使用我的parseConfig函数,但为了使用它,我必须再次导入Text.Pa

我刚刚进入我的第一个真正的Haskell项目(一个web应用程序),我开始遇到来自第三方库的类型泄漏到我的代码中的问题。下面是一个简单的例子:

我的
Parser
模块导入
Test.Parsec
,并导出一个函数(
parseConfig
),该函数返回
ParseError DbConfig
,其中
ParseError
是在
Parsec
库中定义的数据类型(
DbConfig
是我的应用程序的自定义数据类型,为简洁起见未显示)

稍后,我想使用我的
parseConfig
函数,但为了使用它,我必须再次导入
Text.Parsec
,以便访问
ParseError
类型

-- Api.hs
module Api where

import Parser
import Text.Parsec

getConfigFromBody :: Object -> Either ParseError DbConfig
getConfigFromBody = parseConfig . (...)
就管理进口而言,这不仅是一个麻烦,而且关注点的分离也很差,所以我知道这不是最好的方法。我的问题是,管理这个问题的最佳做法是什么?制作类型同义词是否理想

type ConfigParseError = ParseError

parseConfig :: String -> Either ConfigParseError DbConfig
parseConfig = parse ...
这似乎是合理的,因为将
Parsec
依赖项保留在我的
Parser
模块的内部,但是在默认情况下使用别名库类型似乎是一种奇怪的模式


所以我的问题是,大型Haskell应用程序或库如何处理这个问题?管理第三方库中的数据类型是否有一种通用技术?

我不准备给出一个很好的最佳实践列表,但对于初学者来说,如果您想保持内容的有序性,请使用显式导出,而不仅仅是导出所有内容,例如:

module Parser
  ( parseConfig
  ) where
...
显式导出还允许您重新导出导入,例如

module Parser
  ( parseConfig
  , ParseError(..)
  ) where
...
在这之后,您可以只导入解析器,并使解析器可用,就好像它是在解析器中定义的一样


我想这应该可以解决您眼前的问题。

谢谢您的回复。因此,这绝对是有道理的,而且很有帮助。这仍然感觉有点奇怪,主要是因为我的模块公开了来自第三方模块的数据类型。如果我想交换我的解析库,理想情况下,我只需要在解析器中对其进行更改,而没有其他模块需要关心甚至注意到该更改。好的,如果您希望能够交换解析库,则不应该将特定于库的ParseError用作解析函数的输出。如果你不使用它,那么你的问题也会得到解决,因为没有任何东西可以再出口。那会是什么样子呢?这是否意味着创建我自己的错误,捕获任何
ParseError
s,然后返回我自己的错误类型?我想这就是我首先想知道的——这是一种标准实践吗?我认为这里没有标准实践,但提供自己的错误类型很有意义。例如,如果您的配置文件由格式为“key=value”的语句组成,其中key以[a-z]开头,则模块的用户希望得到一个特定的错误
InvalidKey“1mykey”
,而不是毫无意义的(就DBConfig而言)Parsec消息。
module Parser
  ( parseConfig
  , ParseError(..)
  ) where
...