Se ven iguales en el sitio de la aplicación, pero son diferentes, por supuesto. Cuando aplica cualquiera de esas dos funciones, map
o 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 a
produce una lista de valores de cualquier tipo b
. Aunque polimórfica ( a
y b
en la definición anterior representan cualquier tipo), la map
función está destinada a aplicarse a una lista de valores que es solo un tipo de datos posible entre muchos otros en Haskell. La map
funció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 map
funció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 map
over the tail hasta que la lista esté vacía. Vale la pena notar que la implementación de la map
función no depende de ninguna otra función, sino de sí misma.
fmap
Ahora intente consultar información sobre fmap
y verá algo bastante diferente.
Prelude> :info fmap
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
...
-- Defined in ‘GHC.Base’
Este tiempo fmap
se define como una de las funciones cuyas implementaciones deben proporcionar aquellos tipos de datos que deseen pertenecer a la Functor
clase 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 fmap
función. Eso lo hace fmap
aplicable 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 fmap
función es la proporcionada por el Maybe
tipo 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 map
función.
Conclusión
La map
función se puede aplicar nada más que a una lista de valores (donde los valores son de cualquier tipo) mientras que la fmap
funció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 fmap
se puede aplicar y producir el mismo resultado que map
.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)