Hanno lo stesso aspetto sul sito dell'applicazione ma sono diversi, ovviamente. Quando applichi una di queste due funzioni, mapo fmap, a un elenco di valori, produrranno lo stesso risultato, ma ciò non significa che siano destinati allo stesso scopo.
Esegui una sessione GHCI (il Glasgow Haskell Compiler Interactive) per richiedere informazioni su queste due funzioni, quindi dai un'occhiata alle loro implementazioni e scoprirai molte differenze.
carta geografica
Interrogare GHCI per informazioni su map
Prelude> :info map
map :: (a -> b) -> [a] -> [b] -- Defined in ‘GHC.Base’
e la vedrai definita come una funzione di ordine elevato applicabile a un elenco di valori di qualsiasi tipo che aproduce un elenco di valori di qualsiasi tipo b. Sebbene polimorfica (la ae bnella definizione precedente stanno per qualsiasi tipo), la mapfunzione è pensata per essere applicata a un elenco di valori che è solo un possibile tipo di dati tra molti altri in Haskell. La mapfunzione non può essere applicata a qualcosa che non è un elenco di valori.
Come puoi leggere dal codice sorgente di GHC.Base , la mapfunzione è implementata come segue
map _ [] = []
map f (x:xs) = f x : map f xs
che fa uso del pattern matching per estrarre la testa (la x) dalla coda (la xs) della lista, quindi costruisce una nuova lista usando il :costruttore del valore (contro) in modo da anteporre f x(leggilo come "f applicato a x" ) alla ricorsione di mapsopra la coda fino a quando l'elenco è vuoto. Vale la pena notare che l'implementazione della mapfunzione non si basa su nessun'altra funzione ma solo su se stessa.
fmap
Ora prova a interrogare per informazioni su fmape vedrai qualcosa di completamente diverso.
Prelude> :info fmap
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
...
-- Defined in ‘GHC.Base’
Questa volta fmapè definita come una delle funzioni le cui implementazioni devono essere fornite da quei tipi di dati che desiderano appartenere alla Functorclasse del tipo. Ciò significa che possono esserci più di un tipo di dati, non solo il tipo di dati "elenco di valori" , in grado di fornire un'implementazione per la fmapfunzione. Ciò rende fmapapplicabile a un insieme molto più ampio di tipi di dati: i funtori davvero!
Come si può leggere dal codice sorgente di GHC.Base , una possibile implementazione della fmapfunzione è quella fornita dal Maybetipo di dato:
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
e un'altra possibile implementazione è quella fornita dal tipo di dati 2-tuple
instance Functor ((,) a) where
fmap f (x,y) = (x, f y)
e un'altra possibile implementazione è quella fornita dal tipo di dati list (ovviamente!):
instance Functor [] where
fmap f xs = map f xs
che si basa sulla mapfunzione.
Conclusione
La mapfunzione può essere applicata a nient'altro che a liste di valori (dove i valori sono di qualsiasi tipo) mentre la fmapfunzione può essere applicata a molti più tipi di dati: tutti quelli che appartengono alla classe funtore (es. Maybes, tuple, liste, ecc. ). Poiché il tipo di dati "elenco di valori" è anche un funtore (poiché fornisce un'implementazione per esso), fmappuò essere applicato anche producendo lo stesso risultato di map.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)