Les fonctions du générateur sont-elles valables dans la programmation fonctionnelle?

17

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 downCounterméthode semble apatride. De plus, un appel downCounteravec 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 counters'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.

Pete
la source
2
Chaque programme détient l'état. La vraie question est de savoir s'il peut être considéré comme un état fonctionnel , ce que j'interprète comme un «état immuable», un état qui ne change pas une fois attribué. Je prétends que la seule façon de faire en sorte qu'un générateur renvoie quelque chose de différent à chaque appel est si un état mutable est impliqué d'une manière ou d'une autre.
Robert Harvey

Réponses:

14

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:

function downCounter(maxValue) {
  return {
    "value": maxValue,
    "next": function () {
      return downCounter(maxValue > 0 ? maxValue - 1 : 0);
     },
  };
}

let counter = downCounter(26);
counter.value; //=> 26
counter.next().value; //=> 25

De toute évidence, le downCounterest 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().valuesera toujours évalué 25peu 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 est 26alors 25, 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:

counter.next().value; //=> 25
otherFunction(counter); // does this consume the counter?
counter.next().value; // what will this be? It depends on the otherFunction()

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:

downCounter :: Int -> [Int]
downCounter maxValue =
  maxValue : (downCounter (max 0 (maxValue - 1)))
-- invoke as "take n (downCounter 26)" to display n elements
amon
la source