导入haskell模块是否是一种合格的做法?

导入haskell模块是否是一种合格的做法?,haskell,Haskell,我知道导入限定名有助于避免名称冲突。我只是从可读性的角度来提问 由于不熟悉haskell标准库,在阅读haskell代码(主要来自在线书籍和教程)时,我发现有一件事很烦人,那就是当我遇到一个函数时,我不知道它是否属于导入的模块,或者稍后将由用户定义 来自C++背景,通常将它称为一个很好的做法,用名字空间调用标准库函数,例如STD::Debug。哈斯克尔也是这样吗?如果没有,您如何克服我上面提到的问题?来自: 始终使用显式导入列表或符合条件的导入进行标准和 第三方图书馆。这使得代码对更改更加健壮

我知道导入限定名有助于避免名称冲突。我只是从可读性的角度来提问

由于不熟悉haskell标准库,在阅读haskell代码(主要来自在线书籍和教程)时,我发现有一件事很烦人,那就是当我遇到一个函数时,我不知道它是否属于导入的模块,或者稍后将由用户定义

来自C++背景,通常将它称为一个很好的做法,用名字空间调用标准库函数,例如STD::Debug。哈斯克尔也是这样吗?如果没有,您如何克服我上面提到的问题?

来自:

始终使用显式导入列表或符合条件的导入进行标准和 第三方图书馆。这使得代码对更改更加健壮 在这些图书馆里。例外:前奏曲


因此,答案是肯定的。使用合格的导入被认为是标准库和第三方库(Prelude除外)的良好实践。但是对于带有符号的中缀函数(比如
),您可能需要显式导入它,因为限定的导入看起来不太好。

我不太喜欢限定名称,因为它们会使代码变得混乱。只有那些使用与前奏曲函数冲突的名称的模块才应该始终被导入,这些模块通常有明确的建议

对于广泛使用的模块,如
Control.Applicative
,没有太多理由不导入不合格的模块;大多数程序员应该知道其中的所有内容。对于不太知名的包中的模块,如果它们执行非常特定的操作,或者为了避免单个名称的冲突,您可以使用显式的导入列表,例如,
导入数据.list(sortBy)
导入系统.Random.Shuffle(shuffleM)
——这样,您就不必在代码中添加限定符,然而,在imports部分查找标识符会立即告诉您它来自何处(这类似于使用std::cout;)。但老实说,我发现将模块加载到ghci并使用它更方便

*ClunkyModule>:i奇怪的函数

看看它的定义


关于合格的导入或明确的导入列表,有一点我倾向于忽略:它们使您的包更加经得起未来的考验。如果某个模块的新版本停止导出您需要的项目,或者另一个模块引入了冲突名称,那么显式导入将立即向您指出问题所在。

我和您的感觉相同。如果我在一个我不熟悉的模块中看到
functionName
,那么我就不知道它来自多个导入中的哪一个。这里的“陌生模块”也可以指我自己过去写的模块!我目前的风格是这样的,但它并不是被普遍接受的。这种风格的用户可能是极少数

import qualified Long.Path.To.Module as M

... use M.functionName ...
或者如果我想更清楚

import qualified Long.Path.To.Module as Module

... use Module.functionName ...
我很少完全符合资格

import qualified Long.Path.To.Module

... use Long.Path.To.Module.functionName ...
然而,我几乎从不限定中缀运算符。

我自己的一套规则

1) 尝试在不重命名的情况下不导入任何符合条件的内容
B.ByteString
Data.ByteString.ByteString
更具可读性。对于真正普遍存在的模块,例如
Control.Monad
,可能会有例外

2) 不要导入整个模块,导入特定的函数/类型/类,除非它们太多。这样,如果有人想找出某个函数的来源,他只需在文件开头搜索该函数的名称

3) 导入密切相关的模块,将其重命名为相同的名称,除非导入的函数发生冲突,或者其中两个模块作为一个整体导入,而没有导入列表


4) 如果可能,尽量避免使用来自不同模块的同名函数,即使这些模块的重命名方式不同。如果有人知道函数
X.foo
的作用,他很可能会被函数
Y.foo
弄糊涂。如果是不可避免的,考虑创建一个单独的、非常小的模块,它既可以导入函数,又可以在不同名称下导出它们。

C++中的问题是,如果使用不合格的调用,ADL可以将您拧过去。在Haskell中,最糟糕的情况是出现歧义错误,除非一个库完全删除该函数,而另一个库添加该函数。在这种情况下,您可能会遇到一个类型错误-如果不是,那么函数很可能是兼容的。我明白您的意思,但我发现在读取时从ghci或文件头查找函数名是相当不方便的,尤其是当文件很长,并且有多个函数存在问题时。一旦我了解了更多的图书馆,也许我会忘记这一点。当我看到像split这样的函数名时,我不知道它是来自Data.List还是用户定义的。不过,Ghci只适用于当前编译的代码。@Ben:如果你得到一段非编译代码,并且应该理解它,那么你真的很不走运。但为什么会有人这样做?@leftaroundabout也许你在一个团队中工作,不得不去接别人正在进行的工作。也许你已经在Hackage上找到了你所需要的库,但是它已经两年没有更新了,你正在尝试将它转发到现代依赖项。也许你只是在接一个半途而废的项目,而你已经有一段时间没做了。(我经常诅咒我过去的自己缺乏远见;我认为这是开发者天生的条件)@Ben True。。。正如我所说,我也不能为自己的这种远见感到自豪。但实际上,通过在cabal文件中正确指定依赖项版本来避免此类问题似乎更简单,因此即使在编译之前,过时的包也会指向依赖项问题。(当然,我也倾向于忽略这种可能性…)下降到3和4。可追溯性问题。