Les questions sont:
- Les générateurs cassent-ils le paradigme de programmation fonctionnelle? Pourquoi ou pourquoi pas?
- Si oui, les générateurs peuvent-ils être utilisés dans la programmation fonctionnelle et comment?
Considérer ce qui suit:
function * downCounter(maxValue) {
yield maxValue;
yield * downCounter(maxValue > 0 ? maxValue - 1 : 0);
}
let counter = downCounter(26);
counter.next().value; // 26
counter.next().value; // 25
// ...etc
La downCounter
méthode semble apatride. De plus, un appel downCounter
avec la même entrée entraînera toujours la même sortie. Cependant, en même temps, l'appel next()
ne produit pas de résultats cohérents.
Je ne sais pas si les générateurs cassent ou non le paradigme de programmation fonctionnelle, car dans cet exemple, il counter
s'agit d'un objet générateur et donc l'appel next()
produirait les mêmes résultats qu'un autre objet générateur créé avec exactement la même chose maxValue
.
De plus, l'appel someCollection[3]
à un tableau retournerait toujours le quatrième élément. De même, appeler next()
quatre fois sur un objet générateur renvoie également toujours le quatrième élément.
Pour plus de contexte, ces questions ont été posées lors du travail sur un kata de programmation . La personne qui a répondu à la question a soulevé la question de savoir si les générateurs pouvaient ou non être utilisés dans la programmation fonctionnelle et s'ils possédaient ou non l'état.
Réponses:
Les fonctions de générateur ne sont pas particulièrement spéciales. Nous pouvons implémenter un mécanisme similaire nous-mêmes en réécrivant la fonction de générateur dans un style basé sur le rappel:
De toute évidence, le
downCounter
est aussi pur et fonctionnel que possible. Il n'y a aucun problème ici.Le protocole générateur utilisé par JavaScript implique un objet mutable. Ce n'est pas nécessaire, voir le code ci-dessus. En particulier, les objets mutables signifient que nous perdons la transparence référentielle - la possibilité de remplacer une expression par sa valeur. Alors que dans mon exemple,
counter.next().value
sera toujours évalué25
peu importe où il se produit et à quelle fréquence nous le répétons, ce n'est pas le cas avec le générateur JS - à un moment donné, il est26
alors25
, et il pourrait vraiment être n'importe quel nombre. Ceci est problématique si nous passons une référence au générateur à une autre fonction:Il est donc clair que les générateurs maintiennent leur état et ne conviennent donc pas à une programmation fonctionnelle «pure». Heureusement, vous n'avez pas à faire de programmation fonctionnelle pure, et vous pouvez être pragmatique à la place. Si les générateurs rendent votre code plus clair, vous devez les utiliser sans mauvaise conscience. Après tout, JavaScript n'est pas un langage fonctionnel pur, contrairement à Haskell par exemple.
Soit dit en passant, dans Haskell, il n'y a pas de différence entre renvoyer une liste et un générateur, car il utilise une évaluation paresseuse:
la source