J'essaye et je ne parviens pas à faire traverse
fonctionner la fonction Data.Traversable
. Je ne peux pas voir son point. Puisque je viens d'un milieu impératif, quelqu'un peut-il me l'expliquer en termes de boucle impérative? Un pseudo-code serait très apprécié. Merci.
99
Réponses:
traverse
est identique àfmap
, sauf qu'il vous permet également d'exécuter des effets pendant que vous reconstruisez la structure de données.Jetez un œil à l'exemple de la
Data.Traversable
documentation.L'
Functor
instance deTree
serait:Il reconstruit l'arborescence entière, en appliquant
f
à chaque valeur.L'
Traversable
instance est presque la même, sauf que les constructeurs sont appelés dans un style applicatif. Cela signifie que nous pouvons avoir des effets (secondaires) lors de la reconstruction de l'arbre. L'applicatif est presque le même que les monades, sauf que les effets ne peuvent pas dépendre des résultats précédents. Dans cet exemple, cela signifie que vous ne pouvez pas faire quelque chose de différent sur la branche droite d'un nœud en fonction des résultats de la reconstruction de la branche gauche par exemple.Pour des raisons historiques, la
Traversable
classe contient également une version monadique detraverse
calledmapM
. À toutes fins utiles,mapM
c'est le même quetraverse
- il existe en tant que méthode distincte parceApplicative
que ce n'est que plus tard devenu une superclasse deMonad
.Si vous l'implémentiez dans un langage impur, ce
fmap
serait la même chosetraverse
, car il n'y a aucun moyen d'éviter les effets secondaires. Vous ne pouvez pas l'implémenter en boucle, car vous devez parcourir votre structure de données de manière récursive. Voici un petit exemple comment je le ferais en Javascript:L'implémenter comme ça vous limite cependant aux effets que le langage permet. Si vous voulez le non-déterminisme (qui est l'instance de liste des modèles applicatifs) et que votre langage ne l'a pas intégré, vous n'avez pas de chance.
la source
Functor
, la partie qui n'est pas paramétrique. La valeur de l'état dansState
, l'échec dansMaybe
etEither
, le nombre d'éléments dans[]
, et bien sûr les effets secondaires externes arbitraires dansIO
. Je m'en fiche en tant que terme générique (comme lesMonoid
fonctions utilisant "vide" et "append", le concept est plus générique que ce que le terme suggère au début) mais c'est assez courant et sert assez bien le but.ap
dépendre les effets des résultats précédents. J'ai reformulé cette remarque en conséquence.traverse
transforme les choses à l'intérieur de aTraversable
enTraversable
des choses «à l'intérieur» de anApplicative
, étant donné une fonction qui faitApplicative
des choses à partir des choses.Utilisons
Maybe
commeApplicative
et listons commeTraversable
. Nous avons d'abord besoin de la fonction de transformation:Donc, si un nombre est pair, nous en obtenons la moitié (à l'intérieur de a
Just
), sinon nous obtenonsNothing
. Si tout se passe "bien", cela ressemble à ceci:Mais...
La raison est que la
<*>
fonction est utilisée pour construire le résultat, et quand l'un des arguments estNothing
, nousNothing
revenons.Un autre exemple:
Cette fonction génère une liste de longueur
x
avec le contenux
, par exemplerep 3
=[3,3,3]
. Quel est le résultattraverse rep [1..3]
?Nous obtenons les résultats partiels
[1]
,[2,2]
et l'[3,3,3]
utilisationrep
. Maintenant , la sémantique des listes commeApplicatives
est « prendre toutes les combinaisons », par exemple ,(+) <$> [10,20] <*> [3,4]
est[13,14,23,24]
."Toutes les combinaisons" de
[1]
et[2,2]
sont deux fois[1,2]
. Toutes les combinaisons de deux fois[1,2]
et[3,3,3]
six fois[1,2,3]
. Donc nous avons:la source
fac n = length $ traverse rep [1..n]
liftA2 (,)
que le formulaire plus génériquetraverse
.Je pense que c'est plus facile à comprendre en termes de
sequenceA
, commetraverse
on peut le définir comme suit.sequenceA
enchaîne les éléments d'une structure de gauche à droite, renvoyant une structure de même forme contenant les résultats.Vous pouvez également penser
sequenceA
à inverser l'ordre de deux foncteurs, par exemple passer d'une liste d'actions à une action renvoyant une liste de résultats.Prend donc une
traverse
certaine structure et s'appliquef
à transformer chaque élément de la structure en un applicatif, il séquence ensuite les effets de ces applicatifs de gauche à droite, renvoyant une structure avec la même forme contenant les résultats.Vous pouvez également le comparer à
Foldable
, qui définit la fonction associéetraverse_
.Vous pouvez donc voir que la principale différence entre
Foldable
etTraversable
est que ce dernier vous permet de conserver la forme de la structure, alors que le premier vous oblige à replier le résultat dans une autre valeur.Un exemple simple de son utilisation consiste à utiliser une liste comme structure traversable et
IO
comme applicatif:Bien que cet exemple soit plutôt peu intéressant, les choses deviennent plus intéressantes lorsqu'il
traverse
est utilisé sur d'autres types de conteneurs, ou en utilisant d'autres applicatifs.la source
sequenceA . fmap
pour les listes équivaut àsequence . map
non?IO
type peut être utilisé pour exprimer des effets secondaires; (2)IO
se trouve être une monade, ce qui s'avère très pratique. Les monades ne sont pas essentiellement liées aux effets secondaires. Il convient également de noter qu'il y a une signification de "effet" qui est plus large que "effet secondaire" dans son sens habituel - une signification qui inclut des calculs purs. Sur ce dernier point, voir aussi Que signifie exactement «efficace» .C'est un peu comme
fmap
, sauf que vous pouvez exécuter des effets dans la fonction mapper, qui modifie également le type de résultat.Imaginez une liste d'entiers représentant ID utilisateur dans une base de données:
[1, 2, 3]
. Si vous voulez quefmap
ces identifiants d'utilisateurs soient des noms d'utilisateur, vous ne pouvez pas utiliser un traditionnelfmap
, car à l'intérieur de la fonction, vous devez accéder à la base de données pour lire les noms d'utilisateur (ce qui nécessite un effet - dans ce cas, en utilisant laIO
monade).La signature de
traverse
est:Avec
traverse
, vous pouvez faire des effets, par conséquent, votre code pour mapper les ID utilisateur aux noms d'utilisateur ressemble à ceci:Il y a aussi une fonction appelée
mapM
:Toute utilisation de
mapM
peut être remplacée partraverse
, mais pas l'inverse.mapM
ne fonctionne que pour les monades, alors quetraverse
c'est plus générique.Si vous voulez juste pour obtenir un effet et non une valeur utile retour, il y a
traverse_
etmapM_
versions de ces fonctions, toutes deux ne tiennent pas compte de la valeur de retour de la fonction et sont un peu plus rapide.la source
traverse
est la boucle. Son implémentation dépend de la structure de données à parcourir. Cela pourrait être une liste, un arbreMaybe
,Seq
(uence), ou tout ce qui a une façon générique d'être traversée par quelque chose comme une boucle for ou fonction récursive. Un tableau aurait une boucle for, une liste une boucle while, un arbre soit quelque chose de récursif ou la combinaison d'une pile avec une boucle while; mais dans les langages fonctionnels, vous n'avez pas besoin de ces commandes de boucle encombrantes: vous combinez la partie interne de la boucle (sous la forme d'une fonction) avec la structure de données de manière plus directe et moins verbeuse.Avec la
Traversable
classe de types, vous pourriez probablement écrire vos algorithmes de manière plus indépendante et polyvalente. Mais selon mon expérience, celaTraversable
n'est généralement utilisé que pour simplement coller des algorithmes aux structures de données existantes. Il est assez agréable de ne pas avoir besoin d'écrire des fonctions similaires pour différents types de données qualifiés.la source