Eles têm a mesma aparência no site do aplicativo, mas são diferentes, é claro. Quando você aplica uma dessas duas funções, map
ou 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, a
resultando em uma lista de valores de qualquer tipo b
. Embora polimórfica (o a
e b
na definição acima representam qualquer tipo), a map
funçã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 map
funçã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 map
funçã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 map
sobre a cauda até que a lista esteja vazia. É importante notar que a implementação da map
função não depende de nenhuma outra função, mas apenas dela mesma.
fmap
Agora tente consultar informações sobre fmap
e 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 à Functor
classe 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 fmap
função. Isso se torna fmap
aplicá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 fmap
função é aquela fornecida pelo Maybe
tipo 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 map
função.
Conclusão
A map
função pode ser aplicada a nada mais do que lista de valores (onde os valores são de qualquer tipo) enquanto a fmap
funçã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 fmap
pode ser aplicado para produzir o mesmo resultado que map
.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)