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, mapou 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 adonnant une liste de valeurs de tout typeb . Bien que polymorphe (le aet bdans la définition ci-dessus représentent n'importe quel type), la mapfonction 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 mapfonction 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 mapfonction 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 mapsur la queue jusqu'à ce que la liste soit vide. Il est intéressant de noter que l'implémentation de la mapfonction 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 fmapest définie comme l'une des fonctions dont les implémentations doivent être fournies par les types de données qui souhaitent appartenir à la Functorclasse 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 fmapfonction. Cela rend fmapapplicable à 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 fmapfonction est celle fournie par le Maybetype 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 mapfonction.
Conclusion
La mapfonction peut être appliquée à rien de plus qu'une liste de valeurs (où les valeurs sont de tout type) alors que la fmapfonction 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 fmappeut être appliqué à produit aussi bien le même résultat que map.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)