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.