Ils se ressemblent sur le site de l'application, mais ils sont différents, bien sûr. Lorsque vous appliquez l'une de ces deux fonctions, map
ou fmap
à une liste de valeurs, elles produiront le même résultat, mais cela ne signifie pas qu'elles sont destinées au même objectif.
Exécutez une session GHCI (Glasgow Haskell Compiler Interactive) pour rechercher des informations sur ces deux fonctions, puis jetez un œil à leurs implémentations et vous découvrirez de nombreuses différences.
carte
Interrogez GHCI pour obtenir des informations sur map
Prelude> :info map
map :: (a -> b) -> [a] -> [b] -- Defined in ‘GHC.Base’
et vous la verrez définie comme une fonction d'ordre élevé applicable à une liste de valeurs de tout type a
donnant une liste de valeurs de tout typeb
. Bien que polymorphe (le a
et b
dans la définition ci-dessus représentent n'importe quel type), la map
fonction est destinée à être appliquée à une liste de valeurs qui n'est qu'un type de données possible parmi beaucoup d'autres dans Haskell. La map
fonction n'a pas pu être appliquée à quelque chose qui n'est pas une liste de valeurs.
Comme vous pouvez le lire sur le code source GHC.Base , la map
fonction est implémentée comme suit
map _ [] = []
map f (x:xs) = f x : map f xs
qui utilise la correspondance de motifs pour retirer la tête (la x
) de la queue (la xs
) de la liste, puis construit une nouvelle liste en utilisant le:
constructeur de valeur (cons) afin de la mettre en préfixe f x
(lisez-la comme "f appliqué à x" ) à la récursion de map
sur la queue jusqu'à ce que la liste soit vide. Il est intéressant de noter que l'implémentation de la map
fonction ne repose sur aucune autre fonction mais uniquement sur elle-même.
fmap
Essayez maintenant de demander des informations sur fmap
et vous verrez quelque chose de très différent.
Prelude> :info fmap
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
...
-- Defined in ‘GHC.Base’
Cette heure fmap
est définie comme l'une des fonctions dont les implémentations doivent être fournies par les types de données qui souhaitent appartenir à la Functor
classe de types. Cela signifie qu'il peut y avoir plus d'un type de données, pas seulement le type de données "liste de valeurs" , capables de fournir une implémentation pour la fmap
fonction. Cela rend fmap
applicable à un ensemble beaucoup plus grand de types de données: les foncteurs en effet!
Comme vous pouvez le lire dans le code source de GHC.Base , une implémentation possible de la fmap
fonction est celle fournie par le Maybe
type de données:
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
et une autre implémentation possible est celle fournie par le type de données 2-tuple
instance Functor ((,) a) where
fmap f (x,y) = (x, f y)
et une autre implémentation possible est celle fournie par le type de données liste (bien sûr!):
instance Functor [] where
fmap f xs = map f xs
qui repose sur la map
fonction.
Conclusion
La map
fonction peut être appliquée à rien de plus qu'une liste de valeurs (où les valeurs sont de tout type) alors que la fmap
fonction peut être appliquée à beaucoup plus de types de données: tous ceux qui appartiennent à la classe des foncteurs (par exemple, maybes, tuples, listes, etc. ). Étant donné que le type de données «liste de valeurs» est également un foncteur (car il en fournit une implémentation), alors fmap
peut être appliqué à produit aussi bien le même résultat que map
.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)