Quel est l'intérêt de la carte dans Haskell, quand il y a fmap?

97

Partout où j'ai essayé d'utiliser map, fmapa également fonctionné. Pourquoi les créateurs de Haskell ont-ils ressenti le besoin d'une mapfonction? Ne pourrait-il pas être simplement ce qui est actuellement connu fmapet fmappourrait être supprimé de la langue?

Clark Gaebel
la source
11
Je pense que vous vous demandez «à quoi sert fmap dans Haskell»?
zw324 du
11
Je sais à quoi ça fmapsert. Il s'agit de mapper une fonction sur une instance de Functor. Je me demande quel est le but de la spécialisation map.
Clark Gaebel

Réponses:

95

Je voudrais faire une réponse pour attirer l'attention sur le commentaire d' Augusts :

Ce n'est pas comme ça que ça se passe. Ce qui s'est passé, c'est que le type de carte a été généralisé pour couvrir Functor dans Haskell 1.3. Ie, dans Haskell 1.3 fmap s'appelait map. Ce changement a ensuite été annulé dans Haskell 1.4 et fmap a été introduit. La raison de ce changement était d'ordre pédagogique; lors de l'enseignement de Haskell aux débutants, le type très général de carte rend les messages d'erreur plus difficiles à comprendre. À mon avis, ce n'était pas la bonne façon de résoudre le problème.

Haskell 98 est vu comme un pas en arrière par certains Haskeller (dont moi), les versions précédentes ayant défini une bibliothèque plus abstraite et cohérente. Tant pis.

Luqui
la source
16
Ces étapes en arrière sont-elles collectées et documentées n'importe où? Il serait intéressant de voir ce qui a été considéré comme un pas en arrière et s’il existe également de meilleures solutions.
Davorak
3
Le map and fmapexiste depuis longtemps - il a été réchauffé sur la liste de diffusion Haskell-prime en août 2006 - haskell.org/pipermail/haskell-prime/2006-August/thread.html . En contrepoint, je préfère le statu quo. Pour moi, il me semble utile qu'il existe un sous-ensemble de Haskell qui correspond à peu près à Miranda. Au Royaume-Uni, Miranda était utilisée comme langue d'enseignement pour les étudiants en mathématiques et pas seulement pour les étudiants en informatique. Si ce créneau n'est pas déjà perdu dans un langage non fonctionnel (par exemple Mathematica), je ne vois pas Haskell avec un mapremplissage unifié .
stephen tetley du
35
Et je voudrais en outre noter, pour quiconque ne le sait pas déjà, que l'auguste est Lennart Augustsson, qui, à toutes fins pratiques, a fait partie de la communauté Haskell depuis avant que Haskell n'existe, cf. Une histoire d'Haskell , donc le commentaire en question n'est en aucun cas du ouï-dire de seconde main!
CA McCann
3
Il y a maintenant une page Nitpicks sur le wiki Haskell où ce problème est mentionné.
Alexey le
C'est drôle que cette décision ait été prise pour des raisons pédagogiques, car j'ai tout le temps confondu fmap et flatmap lors de l'apprentissage de Haskell. Ils auraient dû réunir un groupe de discussion n00b. :)
Danny Andrews
27

Citant la Functordocumentation sur https://wiki.haskell.org/Typeclassopedia#Functor

Vous pourriez vous demander pourquoi nous avons besoin d'une mapfonction distincte . Pourquoi ne pas simplement supprimer la mapfonction de liste uniquement actuelle et renommer fmapà la map place? Eh bien, c'est une bonne question. L'argument habituel est que quelqu'un qui vient d'apprendre Haskell, lorsqu'il ne l'utilise mappas correctement, préfère de loin voir une erreur sur les listes plutôt que sur environ Functor.

Andrei Bozantan
la source
1
Cela a beaucoup de sens pour moi.
hbobenicio
25

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)
Paolo Angioletti
la source