Haskell 模棱两可的出现——如何避免?

Posted

技术标签:

【中文标题】Haskell 模棱两可的出现——如何避免?【英文标题】:Haskell Ambiguous Occurrences -- how to avoid? 【发布时间】: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

【问题讨论】:

【参考方案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

【讨论】:

以上是关于Haskell 模棱两可的出现——如何避免?的主要内容,如果未能解决你的问题,请参考以下文章

使用 haskell 读取和类型类 - 模棱两可的类型变量错误

为什么Haskell说这是模棱两可的?

如何避免在 Angular 测试阶段出现以下问题:Chrome Headless has not capture in 60000 ms, kill

在 Haskell 中编写 AI Solver 时的类型变量不明确

如何避免Haskell空间泄漏? [关闭]

haskell 列表中的唯一元素