2010-01-31 18 views
36

hago lo siguiente en GHCi:Haskell Ocurrencias ambiguas: ¿cómo evitarlo?

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

GHCi sabe que es un mapa (Mapa Entero Entero). Entonces, ¿por qué reclama una ambigüedad entre Prelude.lookup y Data.Map.lookup cuando el tipo es claro y puedo evitarlo?

<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 

Respuesta

46

Los tipos son claramente diferentes, pero Haskell no permite la sobrecarga ad hoc de nombres, por lo que sólo pueden elegir uno lookup para ser utilizado sin un prefijo.

La solución típica es importar Data.Map cualificado:

> import qualified Data.Map as Map 

entonces se puede decir

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

Por lo general, las bibliotecas de Haskell o bien evitar la re-uso de nombres desde el preludio, o bien reutilizar un montón de ellos. Data.Map es uno de los segundos, y los autores esperan que lo importe calificado.

[Editar para incluir el comentario de ephemient]

Si desea utilizar Data.Map.lookup sin el prefijo, que tiene que ocultar Prelude.lookup ya que está implícita importada de otro modo:

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

Esto es un poco raro, pero podría ser útil si usa Data.Map.lookup un montón y sus estructuras de datos son todos mapas, nunca alistas.

18

En una nota ligeramente más general, esto es algo que me confundió al principio - es así, permítanme reiterar y destacar algo Nathan Sanders dijo:

Haskell no permite la sobrecarga ad hoc de nombres

Esto es cierto por defecto, pero parece sorprendentemente no obvio al principio. Haskell permite dos estilos de polymorphic functions:

  • paramétrico polimorfismo, que permite una función para operar en tipos arbitrarios de una manera estructuralmente idéntico, extracto
  • polimorfismo Ad-hoc, que permite una función para operar en cualquiera de un conjunto definido de tipos en una estructuralmente distinta pero, con suerte, semánticamente manera idéntica

polimorfismo paramétrico es el estándar (y preferido dado una opción) enfoque en Haskell una nd idiomas relacionados; el polimorfismo ad-hoc es el estándar en la mayoría de los otros idiomas, con nombres como "sobrecarga de funciones", y a menudo se implementa en la práctica al escribir múltiples funciones con el mismo nombre.

polimorfismo Ad-hoc está habilitado en Haskell por clases de tipo, que requieren la clase que se define con todas sus funciones polimórficas ad-hoc asociados, y los casos que se declare explícitamente de los tipos utilizados en la resolución de sobrecarga.Las funciones definidas fuera de una declaración de instancia nunca son polimórficas ad-hoc, incluso si sus tipos son lo suficientemente distintos como para que una referencia no sea ambigua.

Por lo tanto, cuando las funciones múltiples que no son de la clase de tipo con nombres idénticos se definen en diferentes módulos, la importación de los dos módulos no cualificados dará lugar a errores si intenta utilizar cualquiera de las funciones. Las combinaciones de Data.List, Data.Map y Data.Set son particularmente atroces en este sentido, y como las partes de Data.List son exportadas por el Preludio, la práctica estándar es (como dice Nathan Sanders) importar siempre las otras calificadas.

+0

Este es el tipo de respuesta que busqué, +1. Pero me queda una pregunta. ¿Por qué, entonces, no existe una clase de tipo "contenedor" para todos estos 'Data.List',' Data.Set', etc.? O si existe (y si entiendo correctamente, esta es la clase de tipo 'Functor') - entonces, ¿por qué la definición de sus instancias para tipos de contenedor no es tan omnipresente en las bibliotecas? – ulidtko

+0

@ulidtko: La respuesta corta es "porque es más difícil de lo que parece", y la respuesta larga no cabría en un comentario. Hay muchas complicaciones involucradas en qué contenedores admiten qué operaciones y límites en los tipos de elementos, & c. Busque información en la extensión 'TypeFamilies' - las API de contenedor son un ejemplo motivador para ello. –

+3

@ulidtko Esto podría ser interesante para ti: http://hackage.haskell.org/packages/archive/classy-prelude/0.4.1/doc/html/ClassyPrelude.html –

Cuestiones relacionadas