Haskell Ambiguous Occurrences -- how to avoid?

On a slightly more general note, this is something that confused me at first--so, let me reiterate and emphasize something Nathan Sanders said:

Haskell doesn't allow ad-hoc overloading of names

This is true by default, but seems surprisingly non-obvious at first. Haskell allows two styles of polymorphic functions:

  • Parametric polymorphism, which allows a function to operate on arbitrary types in a structurally identical, abstract manner
  • Ad-hoc polymorphism, which allows a function to operate on any of a defined set of types in a structurally distinct but, hopefully, semantically identical manner

Parametric polymorphism is the standard (and preferred given a choice) approach in Haskell and related languages; ad-hoc polymorphism is the standard in most other languages, going by names such as "function overloading", and is often implemented in practice by writing multiple functions with the same name.

Ad-hoc polymorphism is enabled in Haskell by type classes, which require the class to be defined with all of its associated ad-hoc polymorphic functions, and instances to be explicitly declared for the types used by overload resolution. Functions defined outside of an instance declaration are never ad-hoc polymorphic, even if their types are sufficiently distinct that a reference would be unambiguous.

So, when multiple non-type-class functions with identical names are defined in different modules, importing both modules unqualified will result in errors if you try to use either function. Combinations of of Data.List, Data.Map, and Data.Set are particularly egregious in this regard, and because parts of Data.List are exported by the Prelude, the standard practice is (as Nathan Sanders says) to always import the others qualified.


The types are clearly different but Haskell doesn't allow ad-hoc overloading of names, so you can only choose one lookup to be used without a prefix.

The typical solution is to import Data.Map qualified:

> import qualified Data.Map as Map

Then you can say

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

Usually, Haskell libraries either avoid re-using names from the Prelude, or else re-use a whole bunch of them. Data.Map is one of the second, and the authors expect you to import it qualified.

[Edit to include ephemient's comment]

If you want to use Data.Map.lookup without the prefix, you have to hide Prelude.lookup since it's implicitly imported otherwise:

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

This is a bit weird but might be useful if you use Data.Map.lookup a whole bunch and your data structures are all maps, never alists.

Tags:

Haskell

Map