Qual é o objetivo do map em Haskell, quando há fmap?

97

Onde quer que eu tentei usar map, fmaptem funcionado bem. Por que os criadores de Haskell sentiram a necessidade de uma mapfunção? Não poderia ser apenas o que é conhecido atualmente fmape fmappoderia ser removido da linguagem?

Clark Gaebel
fonte
11
Acho que você está perguntando 'Qual é o objetivo do fmap em Haskell'?
zw324
11
Eu sei qual é o objetivo disso fmap. É para mapear uma função em uma instância do Functor. Estou me perguntando sobre o propósito da especialização para map.
Clark Gaebel

Respostas:

95

Gostaria de dar uma resposta para chamar a atenção para o comentário de augusts :

Não é assim que acontece. O que aconteceu foi que o tipo de mapa foi generalizado para cobrir o Functor em Haskell 1.3. Ou seja, em Haskell 1.3 fmap era chamado de mapa. Esta mudança foi então revertida no Haskell 1.4 e o fmap foi introduzido. A razão para essa mudança foi pedagógica; ao ensinar Haskell para iniciantes, o tipo muito geral de mapa tornava as mensagens de erro mais difíceis de entender. Na minha opinião, essa não era a maneira certa de resolver o problema.

Haskell 98 é visto como um retrocesso por alguns Haskellers (incluindo eu), versões anteriores tendo definido uma biblioteca mais abstrata e consistente. Ah bem.

luqui
fonte
16
Essas etapas anteriores são coletadas e documentadas em algum lugar? Seria interessante ver o que mais foi considerado um passo atrás e se existem soluções melhores para eles também.
Davorak
3
O map and fmapjá existe há muito tempo - foi reaquecido na lista de discussão Haskell-prime em agosto de 2006 - haskell.org/pipermail/haskell-prime/2006-August/thread.html . Como contraponto, prefiro o status quo. Para mim, parece valioso que haja um subconjunto de Haskell que corresponde aproximadamente a Miranda. No Reino Unido, Miranda foi usada como linguagem de ensino para alunos de matemática, não apenas para alunos de ciência da computação. Se esse nicho ainda não foi perdido para uma linguagem não funcional (por exemplo, Mathematica), não vejo Haskell com um mappreenchimento unificado .
stephen tetley
35
E eu gostaria ainda de observar, para quem ainda não sabe, que augusts é Lennart Augustsson, que para todos os fins práticos tem feito parte da comunidade Haskell desde antes de Haskell existir, cf. Uma História de Haskell , então o comentário em questão não é, de forma alguma, boato de segunda mão!
CA McCann
3
Agora existe a página Nitpicks no wiki Haskell onde este problema é mencionado.
Alexey
É engraçado que essa decisão tenha sido tomada por razões pedagógicas, porque eu confundi fmap com flatmap o tempo todo enquanto aprendia Haskell. Eles deveriam ter obtido um grupo de foco n00b juntos. :)
Danny Andrews
27

Citando a Functordocumentação em https://wiki.haskell.org/Typeclassopedia#Functor

Você pode perguntar por que precisamos de uma mapfunção separada . Por que não simplesmente eliminar a mapfunção somente de lista atual e renomear fmappara map ? Bem, essa é uma boa pergunta. O argumento usual é que alguém que acabou de aprender Haskell, ao usar mapincorretamente, prefere ver um erro sobre listas do que sobre Functor.

Andrei Bozantan
fonte
1
Isso faz muito sentido para mim.
hbobenicio
25

Eles têm a mesma aparência no site do aplicativo, mas são diferentes, é claro. Quando você aplica uma dessas duas funções, mapou fmap, a uma lista de valores, eles produzirão o mesmo resultado, mas isso não significa que tenham o mesmo propósito.

Execute uma sessão GHCI (o Glasgow Haskell Compiler Interactive) para consultar informações sobre essas duas funções, então dê uma olhada em suas implementações e você descobrirá muitas diferenças.

mapa

Consulte GHCI para obter informações sobre map

Prelude> :info map
map :: (a -> b) -> [a] -> [b]   -- Defined in ‘GHC.Base’

e você o verá definido como uma função de alta ordem aplicável a uma lista de valores de qualquer tipo, aresultando em uma lista de valores de qualquer tipo b. Embora polimórfica (o ae bna definição acima representam qualquer tipo), a mapfunção se destina a ser aplicada a uma lista de valores que é apenas um tipo de dados possível entre muitos outros em Haskell. A mapfunção não pôde ser aplicada a algo que não seja uma lista de valores.

Como você pode ler no código-fonte do GHC.Base , a mapfunção é implementada da seguinte maneira

map _ []     = []
map f (x:xs) = f x : map f xs

que faz uso da correspondência de padrões para puxar a ponta (a x) da cauda (a xs) da lista e, em seguida, constrói uma nova lista usando o :construtor de valor (cons) para prefixar f x(leia-o como "f aplicado a x" ) para a recursão de mapsobre a cauda até que a lista esteja vazia. É importante notar que a implementação da mapfunção não depende de nenhuma outra função, mas apenas dela mesma.

fmap

Agora tente consultar informações sobre fmape você verá algo bem diferente.

Prelude> :info fmap
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b
  ...
  -- Defined in ‘GHC.Base’

Este tempo fmapé definido como uma das funções cujas implementações devem ser fornecidas por aqueles tipos de dados que desejam pertencer à Functorclasse de tipo. Isso significa que pode haver mais de um tipo de dados, não apenas o tipo de dados "lista de valores" , capaz de fornecer uma implementação para a fmapfunção. Isso se torna fmapaplicável a um conjunto muito maior de tipos de dados: os functores, de fato!

Como você pode ler no código-fonte do GHC.Base , uma possível implementação da fmapfunção é aquela fornecida pelo Maybetipo de dados:

instance  Functor Maybe  where
  fmap _ Nothing       = Nothing
  fmap f (Just a)      = Just (f a)

e outra implementação possível é aquela fornecida pelo tipo de dados de 2 tuplas

instance Functor ((,) a) where
  fmap f (x,y) = (x, f y)

e outra implementação possível é aquela fornecida pelo tipo de dados de lista (claro!):

instance  Functor []  where
  fmap f xs = map f xs

que depende da mapfunção.

Conclusão

A mapfunção pode ser aplicada a nada mais do que lista de valores (onde os valores são de qualquer tipo) enquanto a fmapfunção pode ser aplicada a muito mais tipos de dados: todos aqueles que pertencem à classe do functor (por exemplo, maybes, tuplas, listas, etc. ) Uma vez que o tipo de dados "lista de valores" também é um functor (porque fornece uma implementação para ele), então fmappode ser aplicado para produzir o mesmo resultado que map.

map  (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)
Paolo Angioletti
fonte