2010-01-31 5 views
36

Je le suivais dans GHCi:Haskell Ambiguous Occurrences - comment éviter?

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

GHCi sait que la carte est un (Carte Entier Entier). Alors pourquoi revendique-t-il une ambiguïté entre Prelude.lookup et Data.Map.lookup lorsque le type est clair et que je peux éviter?

<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 

Répondre

46

Les types sont clairement différents, mais Haskell ne vous ne pouvez pas permettre la surcharge des noms ad hoc, afin de choisir un lookup à utiliser sans préfixe.

La solution classique consiste à importer Data.Map qualifiés:

> import qualified Data.Map as Map 

alors vous pouvez dire

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

Habituellement, les bibliothèques Haskell soit éviter de réutiliser les noms du Prélude, ou bien la réutilisation tout un tas d'entre eux. Data.Map est l'un des seconds, et les auteurs s'attendent à ce que vous l'importiez qualifié.

[Editer pour inclure le commentaire de ephemient]

Si vous souhaitez utiliser Data.Map.lookup sans le préfixe, vous devez cacher Prelude.lookup car il est implicitement importé autrement:

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

C'est un peu bizarre, mais peut-être être utile si vous utilisez Data.Map.lookup tout un tas et vos structures de données sont toutes des cartes, ne s'alignent jamais.

18

Sur une note un peu plus générale, c'est quelque chose qui me confondre dans un premier temps - alors, laissez-moi répéter et souligner quelque chose Nathan Sanders a dit:

Haskell ne permet pas de noms surcharge ad hoc

Ceci est vrai par défaut, mais semble étonnamment non évident au premier abord. Haskell permet à deux styles de polymorphic functions:

  • polymorphisme Parametric, ce qui permet une fonction d'opérer sur les types arbitraires d'une, de manière abstraite structurellement identique
  • polymorphisme ad hoc, qui permet une fonction de fonctionner sur n'importe quel ensemble défini de types d'une manière structurellement distincte mais, espérons-le, sémantiquement identique

Le polymorphisme paramétrique est l'approche standard (et préféré donné un choix) dans Haskell a nd langues apparentées; Le polymorphisme ad hoc est la norme dans la plupart des autres langages, passant par des noms tels que "surcharge de fonction", et est souvent implémenté en pratique en écrivant plusieurs fonctions avec le même nom.

polymorphisme ad hoc est activé dans Haskell par classes de type, qui exigent la classe à définir avec toutes ses fonctions polymorphes ad hoc associés et instances à déclarer explicitement les types utilisés par la résolution de surcharge.Les fonctions définies en dehors d'une déclaration d'instance ne sont jamais polymorphe ad-hoc, même si leurs types sont suffisamment distincts pour qu'une référence soit sans ambiguïté. Ainsi, lorsque plusieurs fonctions de type non-classe avec des noms identiques sont définies dans des modules différents, l'importation des deux modules non qualifiés entraînera des erreurs si vous essayez d'utiliser l'une ou l'autre de ces fonctions. Les combinaisons de Data.List, Data.Map et Data.Set sont particulièrement flagrantes à cet égard, et parce que des parties de Data.List sont exportées par le Prelude, la pratique standard est (comme le dit Nathan Sanders) d'importer toujours les autres qualifiés.

+0

C'est le genre de réponse que j'ai cherché, +1. Mais j'ai une question à gauche. Pourquoi, alors, il n'y a pas de classe de type "container" pour tous ces 'Data.List',' Data.Set' etc? Ou s'il y a (et si je comprends bien, c'est la classe de type 'Functor') - alors, pourquoi la définition de ses instances pour les types de conteneur n'est pas omniprésente dans les bibliothèques? – ulidtko

+0

@ulidtko: La réponse courte est "parce que c'est plus difficile que ça en a l'air", et la réponse longue ne rentrerait pas dans un commentaire. Il y a beaucoup de complications impliquées dans ce que les conteneurs supportent quelles opérations et limites sur les types d'éléments, & c. Rechercher des informations sur l'extension 'TypeFamilies' - les API de conteneur en sont un exemple motivant. –

+3

@ulidtko Cela pourrait être intéressant pour vous: http://hackage.haskell.org/packages/archive/classy-prelude/0.4.1/doc/html/ClassyPrelude.html –