lenguaje ambiguo ambiguedad haskell map

haskell - ambiguedad - ambiguo wikipedia



Haskell Ocurrencias ambiguas: ¿cómo evitarlo? (2)

Hago lo siguiente en GHCI:

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

GHCI sabe que el mapa es un (entero de mapa 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


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

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 funciones polimórficas :

  • Polimorfismo paramétrico , que permite que una función opere en tipos arbitrarios de una manera estructuralmente idéntica y abstracta
  • Polimorfismo Ad-hoc , que permite que una función opere en cualquiera de un conjunto definido de tipos de una manera estructuralmente distinta pero, con suerte, semánticamente idéntica

El polimorfismo paramétrico es el enfoque estándar (y preferido si se elige) en Haskell y en los 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.

El polimorfismo Ad-hoc se habilita en Haskell por clases de tipo , que requieren que la clase se defina con todas sus funciones polimórficas ad-hoc asociadas, y las instancias se declaren explícitamente para los tipos utilizados por 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 se definen múltiples funciones no de clase de tipo con nombres idénticos en módulos diferentes, la importación de ambos módulos no calificados generará errores si intenta utilizar cualquiera de las funciones. Las combinaciones de Data.List , Data.Map y Data.Set son particularmente atroces en este aspecto, y debido a que las partes de Data.List son exportadas por el Prelude, la práctica estándar es (como dice Nathan Sanders) importar siempre las otras calificado.


Los tipos son claramente diferentes, pero Haskell no permite la sobrecarga ad-hoc de nombres, por lo que solo puede elegir una lookup para usar sin un prefijo.

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

> import qualified Data.Map as Map

Entonces puedes decir

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

Por lo general, las bibliotecas de Haskell evitan volver a utilizar los nombres del Preludio, o bien reutilizar un montón de ellas. Data.Map es uno de los segundos, y los autores esperan que lo importes calificado.

[Editar para incluir el comentario de ephemient]

Si desea utilizar Data.Map.lookup sin el prefijo, debe ocultar Prelude.lookup ya que implícitamente se importa lo contrario:

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

Esto es un poco extraño, pero podría ser útil si usa Data.Map.lookup por completo y sus estructuras de datos son todos mapas, nunca lo son.