Hanno lo stesso aspetto sul sito dell'applicazione ma sono diversi, ovviamente. Quando applichi una di queste due funzioni, map
o 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 a
produce un elenco di valori di qualsiasi tipo b
. Sebbene polimorfica (la a
e b
nella definizione precedente stanno per qualsiasi tipo), la map
funzione è pensata per essere applicata a un elenco di valori che è solo un possibile tipo di dati tra molti altri in Haskell. La map
funzione non può essere applicata a qualcosa che non è un elenco di valori.
Come puoi leggere dal codice sorgente di GHC.Base , la map
funzione è 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 map
sopra la coda fino a quando l'elenco è vuoto. Vale la pena notare che l'implementazione della map
funzione non si basa su nessun'altra funzione ma solo su se stessa.
fmap
Ora prova a interrogare per informazioni su fmap
e 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 Functor
classe 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 fmap
funzione. Ciò rende fmap
applicabile 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 fmap
funzione è quella fornita dal Maybe
tipo 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 map
funzione.
Conclusione
La map
funzione può essere applicata a nient'altro che a liste di valori (dove i valori sono di qualsiasi tipo) mentre la fmap
funzione 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), fmap
può essere applicato anche producendo lo stesso risultato di map
.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)