【问题标题】:Haskell Ambiguous Occurrences -- how to avoid?Haskell 模棱两可的出现——如何避免?
【发布时间】:2010-01-31 22:24:56
【问题描述】:

我在 GHCI 中执行以下操作:

:m + Data.Map
let map = fromList [(1, 2)]
lookup 1 map

GHCI 知道 map 是一个 (Map Integer Integer)。那么,当类型明确并且我可以避免时,为什么它声称 Prelude.lookup 和 Data.Map.lookup 之间存在歧义?

<interactive>:1:0:
    Ambiguous occurrence `lookup'
    It could refer to either `Prelude.lookup', imported from Prelude
                          or `Data.Map.lookup', imported from Data.Map

> :t map
map :: Map Integer Integer
> :t Prelude.lookup
Prelude.lookup :: (Eq a) => a -> [(a, b)] -> Maybe b
> :t Data.Map.lookup
Data.Map.lookup :: (Ord k) => k -> Map k a -> Maybe a

【问题讨论】:

    标签: haskell map


    【解决方案1】:

    类型明显不同,但 Haskell 不允许临时重载名称,因此您只能选择一个 lookup 来使用而不使用前缀。

    典型的解决方案是导入Data.Mapqualified:

    > import qualified Data.Map as Map
    

    那你可以说

    > lookup 1 [(1,2), (3,4)]
    Just 2
    > Map.lookup 1 Map.empty
    Nothing
    

    通常,Haskell 库要么避免重用 Prelude 中的名称,要么重用一大堆。 Data.Map 是第二个之一,作者希望你导入它是合格的。

    [编辑以包含 ehemient 的评论]

    如果你想使用不带前缀的Data.Map.lookup,你必须隐藏Prelude.lookup,否则它会被隐式导入:

    import Prelude hiding (lookup) 
    import Data.Map (lookup)
    

    这有点奇怪,但如果你使用一大堆 Data.Map.lookup 并且你的数据结构都是映射,而不是 alist,这可能会很有用。

    【讨论】:

      【解决方案2】:

      稍微笼统地说,这让我一开始感到困惑——所以,让我重申并强调 Nathan Sanders 所说的话:

      Haskell 不允许临时重载名称

      默认情况下这是正确的,但乍一看似乎并不明显。 Haskell 允许两种样式的polymorphic functions

      • 参数多态性,它允许函数以结构相同的抽象方式对任意类型进行操作
      • Ad-hoc 多态性,它允许函数以结构上不同但语义相同的方式对任何定义的类型集进行操作

      参数多态是 Haskell 和相关语言中的标准(如果有选择的话,也是首选)方法; ad-hoc 多态性是大多数其他语言的标准,名称为“函数重载”,并且在实践中通常通过编写多个具有相同名称的函数来实现。

      在 Haskell 中通过 类型类 启用了临时多态性,这需要使用所有相关联的临时多态函数来定义类,并为所使用的类型显式声明实例通过重载决议。 在实例声明之外定义的函数永远不会是临时多态的,即使它们的类型足够不同以至于引用是明确的。

      因此,当在不同的模块中定义多个具有相同名称的非类型类函数时,如果您尝试使用其中一个函数,则不合格地导入两个模块都会导致错误。 Data.ListData.MapData.Set 的组合在这方面特别令人震惊,并且因为 Data.List 的部分内容由 Prelude 导出,标准做法是(如 Nathan Sanders 所说)始终导入其他内容合格。

      【讨论】:

      • 这是我寻求的答案,+1。但我还有一个问题。那么,为什么所有这些Data.ListData.Set 等都没有“容器”类型类?或者如果有(如果我理解正确,这是 Functor 类型类)——那么,为什么在库中为容器类型定义它的实例不是那么普遍?
      • @ulidtko:简短的回答是“因为它比听起来更难”,而长答案不适合评论。哪些容器支持哪些操作以及对元素类型的限制等涉及很多复杂性。查找有关 TypeFamilies 扩展的信息——容器 API 是一个激励示例。
      • @ulidtko 这对你来说可能很有趣:hackage.haskell.org/packages/archive/classy-prelude/0.4.1/doc/…
      【解决方案3】:

      这种情况总是使用说明书中的代码发生。你总是可以给他们一个不同的名字。还要注意电子书中嵌入的 unicode 空格。

      sum2 []= 0 
      sum2 (n:ns) = n + sum2 ns
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-04-10
        • 2016-02-19
        • 1970-01-01
        • 1970-01-01
        • 2021-12-21
        • 1970-01-01
        • 2014-08-12
        相关资源
        最近更新 更多