如何使用限定名称处理看起来丑陋的中缀符号

时间:2012-01-28 05:54:56

标签: haskell module namespaces operators

我通常坚信在我编程的大多数语言中都使用名称空间(合格的模块名称),因为很容易一目了然地知道某个标识符的来源。在Haskell中,还有一个额外的优点,即避免使用Prelude函数进行常见的名称冲突。

但是,我觉得必须在中缀符号(或其他短的DSL-y标识符)上放置一个名称空间看起来很奇怪,所以我很想重新出口值,如下所示:

import qualified Data.Sequence as Seq
(|>) = (Seq.|>)
(<|) = (Seq.<|)

现在困扰我的是那个

  • 手动重新出口价值感觉就像无聊的样板。

  • 手动重新导出值围绕现有的模块系统,似乎不适用于数据构造函数(可能还有其他我未遇到过的东西)

    import qualified Data.Sequence as Seq
    (:>) = (Seq.:>)  --gives me a parse error:
                     --"Not in scope: data constructor `:>'"
    

如何协调中缀符号和命名空间?我应该放弃并学习命名空间吗?是否已经建立了关于命名空间和符号的Haskell最佳实践?

2 个答案:

答案 0 :(得分:19)

嗯,你可以做的一件事是导入两次:

import Data.Sequence ((|>), (<|), ViewR ((:>)))
import qualified Data.Sequence as Seq

这将仅导入:>|><|不合格,其他所有内容都合格。请注意,由于:>是数据构造函数,因此您还必须导入其数据类型(ViewR),但必须导入剩余的ViewR构造函数。

此外,如果您担心冲突,您应该只是隐藏操作员:

import Prelude hiding ((.))

如果你正在使用一个理智的库,与Prelude的冲突意味着库函数被设计为替换该Prelude函数(例如Control.Category),所以你想让它替换它默认含义。

就最佳实践而言,我从未见过任何人使用合格的运营商,除非发生冲突或他们在GHCi。总而言之,即使考虑到了解运营商所在位置的优势,也会使代码的可读性降低。

答案 1 :(得分:8)

我通常导入不合格的类型名称,构造函数和运算符,以及其他所有限定条件:

import Data.Sequence (Seq, ViewL(..), ViewR(..), (|>), (<|))
import qualified Data.Sequence as Seq

文档推荐Data.Map和其他标准容器使用这种双重导入的非限定类型名称样式。

但是,您无法始终导入不合格的运算符 - 例如,如果您在同一模块中使用Array / VectorMap,则无法导入来自两个不合格的(!)。在那种情况下,我通常只使用它合格。它看起来很奇怪,但它比其他选项更好(比如为其中一个提出你自己的名字以避免冲突)。当然,如果它阻止人们使用不安全的函数,例如(Data.Map.!):)

,也许这是一件好事