【问题标题】:is importing haskell module as qualified a good practice?将haskell模块导入合格是一种好习惯吗?
【发布时间】:2014-05-08 10:10:51
【问题描述】:

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

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

来自 C++ 背景,使用命名空间调用标准库函数通常被视为一种好习惯,例如 std::find。 haskell 也一样吗?如果不是,那么您如何克服我上面提到的问题?

【问题讨论】:

  • C++ 中的问题是,如果你使用不合格的调用,ADL 可能会搞砸你。在 Haskell 中,您得到的最糟糕的情况是模棱两可的错误,除非一个库完全删除该函数,而另一个库添加它。在这种情况下,您可能会收到类型错误 - 如果没有,这些函数很可能还是兼容的。

标签: haskell


【解决方案1】:

来自Haskell style guide

对于标准和 第三方库。这使代码对更改更加健壮 在这些库中。例外:前奏曲。

所以,答案是肯定的。使用合格的导入被认为是除 Prelude 之外的标准库和第三方库的良好做法。但是对于带有符号的中缀函数(例如<|*|>),您可能需要显式导入它,因为合格的导入看起来不太好。

【讨论】:

    【解决方案2】:

    我不太喜欢限定名称,IMO 他们宁愿把代码弄得乱七八糟。唯一应该始终被导入限定的模块是那些使用与前奏函数冲突的名称的模块——这些模块通常有一个明确的建议,in the documentation

    对于Control.Applicative等广泛使用的模块,没有太多理由不导入不合格的;大多数程序员应该知道里面的所有内容。对于来自不太知名的包的模块,它们做一些非常具体的事情,或者为了避免单个名称的冲突,您可以使用 显式导入列表,例如import Data.List (sortBy)import System.Random.Shuffle (shuffleM) - 这样,您不必在代码中乱扔限定词,但在导入部分中查找标识符会立即告诉您它来自哪里(这类似于 using std::cout;)。但老实说,我发现将模块加载到 ghci 并使用会更方便

    *ClunkyModule> :i 奇怪的函数

    查看它的定义位置。

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

    【讨论】:

    • 我明白你的意思,但我发现在读取时从 ghci 或文件头查找函数名相当不方便,特别是如果文件很长,并且有多个有问题的功能。一旦我了解更多库,也许我会克服这个问题。当我看到像 split 这样的函数名称时,我不知道它是来自 Data.List 还是用户定义的。
    • Ghci 仅适用于当前编译的代码。
    • @Ben:如果您收到一段非编译代码并且应该理解它,那么您确实很不幸。但是为什么会有人这样做呢?
    • @leftaroundabout 也许您在一个团队中工作,并且必须接手其他人正在进行的工作。也许你已经在 Hackage 上找到了你需要的库,但它已经有 2 年没有更新过了,你正试图将它转发到现代依赖项。也许你只是在挑选一个你有一段时间没有做过的半成品项目。 (我经常诅咒过去的自己缺乏远见;我认为这是开发人员与生俱来的)
    • @Ben True... 正如我所说,我也不能为自己的这种远见感到自豪。但实际上,通过在 cabal 文件中正确指定依赖版本来避免此类问题似乎更干净,因此即使在编译之前,过时的包也会指出依赖问题。 (当然我也倾向于忽略这种可能性……)
    【解决方案3】:

    我和你的感觉一样。如果我在一个我不熟悉的模块中看到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 ...
    

    但是,我几乎从不限定中缀运算符。

    【讨论】:

      【解决方案4】:

      我自己的一套规则。

      1) 尽量不要在没有重命名的情况下导入任何合格的东西。 B.ByteStringData.ByteString.ByteString 更具可读性。真正无处不在的模块可以例外,例如Control.Monad

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

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

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

      【讨论】:

      • 下降 3 和 4。可追溯性很重要。
      猜你喜欢
      • 2017-12-09
      • 2019-05-10
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      • 1970-01-01
      • 2022-06-23
      • 2013-03-28
      • 2019-09-26
      相关资源
      最近更新 更多