¿Cuál es el punto de mapa en Haskell, cuando hay fmap?

97

En todos los lugares que he intentado usar map, también fmapha funcionado. ¿Por qué los creadores de Haskell sintieron la necesidad de una mapfunción? ¿No podría ser simplemente lo que se conoce actualmente fmapy fmappodría eliminarse del idioma?

Clark Gaebel
fuente
11
Creo que estás preguntando '¿Cuál es el punto de fmap en Haskell'?
zw324
11
Sé cuál es el punto fmap. Es mapear una función sobre una instancia de Functor. Me pregunto cuál es el propósito de la especialización map.
Clark Gaebel

Respuestas:

95

Me gustaría dar una respuesta para llamar la atención sobre el comentario de augustss :

En realidad, no es así como sucede. Lo que sucedió fue que el tipo de mapa se generalizó para cubrir Functor en Haskell 1.3. Es decir, en Haskell 1.3 fmap se llamaba map. Este cambio luego se revirtió en Haskell 1.4 y se introdujo fmap. La razón de este cambio fue pedagógica; Al enseñar Haskell a principiantes, el tipo de mapa muy general dificultaba la comprensión de los mensajes de error. En mi opinión, esta no era la forma correcta de resolver el problema.

Haskell 98 es visto como un paso atrás por algunos Haskellers (incluyéndome a mí), ya que las versiones anteriores han definido una biblioteca más abstracta y consistente. Oh bien.

luqui
fuente
16
¿Se recopilan y documentan estos pasos hacia atrás en algún lugar? Sería interesante ver qué más se consideró un paso atrás y si también hay mejores soluciones para ellos.
Davorak
3
El map and fmapha existido durante mucho tiempo - fue recalentado en la lista de correo de Haskell-prime en agosto de 2006 - haskell.org/pipermail/haskell-prime/2006-August/thread.html . Como contrapunto, prefiero el statu quo. Para mí, parece valioso que haya un subconjunto de Haskell que se corresponda aproximadamente con Miranda. En el Reino Unido, Miranda se utilizó como lenguaje de enseñanza para estudiantes de matemáticas, no solo para estudiantes de ciencias de la computación. Si ese nicho aún no se ha perdido en un lenguaje no funcional (por ejemplo, Mathematica), no veo a Haskell con un maprelleno unificado .
Stephen Tetley
35
Y me gustaría señalar además, para cualquiera que aún no lo sepa, que augustss es Lennart Augustsson, quien para todos los propósitos prácticos ha sido parte de la comunidad Haskell desde antes de que existiera Haskell, cf. Una historia de Haskell , por lo que el comentario en cuestión no es de ninguna manera un rumor de segunda mano.
CA McCann
3
Ahora hay una página de Nitpicks en la wiki de Haskell donde se menciona este problema.
Alexey
Es curioso que esta decisión se haya tomado por razones pedagógicas, porque confundí fmap con flatmap todo el tiempo cuando aprendí Haskell. Deberían haber reunido un grupo de enfoque n00b. :)
Danny Andrews
27

Citando de la Functordocumentación en https://wiki.haskell.org/Typeclassopedia#Functor

Puede preguntar por qué necesitamos una mapfunción separada . ¿Por qué no simplemente eliminar la función actual de solo lista mapy cambiar el nombre fmapa en su map lugar? Bueno, esa es una buena pregunta. El argumento habitual es que alguien que esté aprendiendo Haskell, cuando lo usa mapincorrectamente, preferiría ver un error sobre las listas que sobre Functor.

Andrei Bozantan
fuente
1
Esto tiene mucho sentido para mí.
hbobenicio
25

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)
Paolo Angioletti
fuente