Se ven iguales en el sitio de la aplicación, pero son diferentes, por supuesto. Cuando aplica cualquiera de esas dos funciones, mapo fmap, a una lista de valores, producirán el mismo resultado, pero eso no significa que estén destinadas al mismo propósito.
Ejecute una sesión de GHCI (el Glasgow Haskell Compiler Interactive) para consultar información sobre esas dos funciones, luego eche un vistazo a sus implementaciones y descubrirá muchas diferencias.
mapa
Consulte GHCI para obtener información sobre map
Prelude> :info map
map :: (a -> b) -> [a] -> [b] -- Defined in ‘GHC.Base’
y verá que se define como una función de orden superior aplicable a una lista de valores de cualquier tipo que aproduce una lista de valores de cualquier tipo b. Aunque polimórfica ( ay ben la definición anterior representan cualquier tipo), la mapfunción está destinada a aplicarse a una lista de valores que es solo un tipo de datos posible entre muchos otros en Haskell. La mapfunción no se pudo aplicar a algo que no sea una lista de valores.
Como puede leer en el código fuente de GHC.Base , la mapfunción se implementa de la siguiente manera
map _ [] = []
map f (x:xs) = f x : map f xs
que hace uso de la coincidencia de patrones para sacar la cabeza (the x) de la cola (the xs) de la lista, luego construye una nueva lista usando el :constructor de valores (contras) para anteponer f x( léalo como "f aplicado ax" ) a la recursividad de mapover the tail hasta que la lista esté vacía. Vale la pena notar que la implementación de la mapfunción no depende de ninguna otra función, sino de sí misma.
fmap
Ahora intente consultar información sobre fmapy verá algo bastante diferente.
Prelude> :info fmap
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
...
-- Defined in ‘GHC.Base’
Este tiempo fmapse define como una de las funciones cuyas implementaciones deben proporcionar aquellos tipos de datos que deseen pertenecer a la Functorclase de tipos. Eso significa que puede haber más de un tipo de datos, no solo el tipo de datos "lista de valores" , capaz de proporcionar una implementación para la fmapfunción. Eso lo hace fmapaplicable a un conjunto mucho mayor de tipos de datos: ¡los functores de hecho!
Como puede leer en el código fuente de GHC.Base , una posible implementación de la fmapfunción es la proporcionada por el Maybetipo de datos:
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
y otra posible implementación es la proporcionada por el tipo de datos de 2 tuplas
instance Functor ((,) a) where
fmap f (x,y) = (x, f y)
y otra posible implementación es la proporcionada por el tipo de datos de la lista (¡por supuesto!):
instance Functor [] where
fmap f xs = map f xs
que se basa en la mapfunción.
Conclusión
La mapfunción se puede aplicar nada más que a una lista de valores (donde los valores son de cualquier tipo) mientras que la fmapfunción se puede aplicar a muchos más tipos de datos: todos aquellos que pertenecen a la clase functor (por ejemplo, maybes, tuplas, listas, etc. ). Dado que el tipo de datos "lista de valores" también es un funtor (porque proporciona una implementación para él), entonces fmapse puede aplicar y producir el mismo resultado que map.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)