La plupart des sources définissent une fonction pure comme ayant les deux propriétés suivantes:
- Sa valeur de retour est la même pour les mêmes arguments.
- Son évaluation n'a pas d'effets secondaires.
C'est la première condition qui me concerne. Dans la plupart des cas, il est facile de juger. Tenez compte des fonctions JavaScript suivantes (comme indiqué dans cet article )
Pur:
const add = (x, y) => x + y;
add(2, 4); // 6
Impur:
let x = 2;
const add = (y) => {
return x += y;
};
add(4); // x === 6 (the first time)
add(4); // x === 10 (the second time)
Il est facile de voir que la 2ème fonction donnera différentes sorties pour les appels suivants, violant ainsi la première condition. Et donc, c'est impur.
Je reçois cette partie.
Maintenant, pour ma question, considérons cette fonction qui convertit un montant donné en dollars en euros:
(EDIT - Utilisation const
dans la première ligne. Utilisé let
plus tôt par inadvertance.)
const exchangeRate = fetchFromDatabase(); // evaluates to say 0.9 for today;
const dollarToEuro = (x) => {
return x * exchangeRate;
};
dollarToEuro(100) //90 today
dollarToEuro(100) //something else tomorrow
Supposons que nous récupérons le taux de change à partir d'une base de données et qu'il change chaque jour.
Maintenant, peu importe combien de fois j'appelle cette fonction aujourd'hui , cela me donnera la même sortie pour l'entrée 100
. Cependant, cela pourrait me donner une sortie différente demain. Je ne sais pas si cela viole la première condition ou non.
IOW, la fonction elle-même ne contient aucune logique pour muter l'entrée, mais elle s'appuie sur une constante externe qui pourrait changer à l'avenir. Dans ce cas, il est absolument certain que cela changera quotidiennement. Dans d'autres cas, cela peut arriver; il se pourrait que non.
Pouvons-nous appeler de telles fonctions des fonctions pures. Si la réponse est NON, comment pouvons-nous la refactoriser pour en faire une?
la source
function myNumber(n) { this.n = n; }; myNumber.prototype.valueOf = function() { console.log('impure'); return this.n; }; const n = new myNumber(42); add(n, 1);
(x) => {return x * 0.9;}
. Demain, vous aurez une fonction différente qui sera aussi pure, peut-être(x) => {return x * 0.89;}
. Notez que chaque fois que vous l'exécutez,(x) => {return x * exchangeRate;}
il crée une nouvelle fonction, et cette fonction est pure carexchangeRate
ne peut pas changer.const dollarToEuro = (x, exchangeRate) => { return x * exchangeRate; };
pour une fonction pure,Its return value is the same for the same arguments.
devrait toujours tenir, 1 seconde, 1 décennie .. plus tard, peu importe quoiRéponses:
La
dollarToEuro
valeur de retour de 'dépend d'une variable externe qui n'est pas un argument; par conséquent, la fonction est impure.Une option consiste à passer
exchangeRate
. De cette façon, tous les arguments de temps sont(something, somethingElse)
, la sortie est garantie d'êtresomething * somethingElse
:Notez que pour la programmation fonctionnelle, vous devez éviter
let
- utilisez toujoursconst
pour éviter la réaffectation.la source
const add = x => y => x + y; const one = add(42);
ici les deuxadd
etone
sont des fonctions pures.const foo = 42; const add42 = x => x + foo;
<- ceci est une autre fonction pure, qui utilise à nouveau des variables libres.dollarToEuro
fonction dans l'exemple de votre réponse est impure car elle dépend de la variable libreexchangeRate
. C'est absurde. Comme l'a souligné zerkms, la pureté d'une fonction n'a rien à voir avec le fait qu'elle ait ou non des variables libres. Cependant, zerkms a également tort car il pense que ladollarToEuro
fonction est impure car elle dépend de celleexchangeRate
qui provient d'une base de données. Il dit que c'est impur parce que "cela dépend de manière transitoire de l'OI".dollarToEuro
c'est impur parce queexchangeRate
c'est une variable libre. Cela suggère que si ceexchangeRate
n'était pas une variable libre, c'est-à-dire si c'était un argument, alors cedollarToEuro
serait pur. Par conséquent, cela suggère quedollarToEuro(100)
c'est impur maisdollarToEuro(100, exchangeRate)
pur. C'est clairement absurde, car dans les deux cas, vous dépendez de ceexchangeRate
qui provient d'une base de données. La seule différence est la présence ou non d'exchangeRate
une variable libre dans ladollarToEuro
fonction.Techniquement, tout programme que vous exécutez sur un ordinateur est impur car il se compile finalement en instructions telles que "déplacer cette valeur dans
eax
" et "ajouter cette valeur au contenu deeax
", qui sont impures. Ce n'est pas très utile.Au lieu de cela, nous pensons à la pureté en utilisant des boîtes noires . Si un code produit toujours les mêmes sorties lorsqu'il reçoit les mêmes entrées, il est considéré comme pur. Selon cette définition, la fonction suivante est également pure même si en interne elle utilise une table de mémo impure.
Nous ne nous soucions pas des composants internes, car nous utilisons une méthodologie de boîte noire pour vérifier la pureté. De même, nous ne nous soucions pas que tout le code soit finalement converti en instructions machine impures parce que nous pensons à la pureté en utilisant une méthodologie de boîte noire. Les internes ne sont pas importants.
Maintenant, considérez la fonction suivante.
La
greet
fonction est-elle pure ou impure? Selon notre méthodologie de boîte noire, si nous lui donnons la même entrée (par exempleWorld
), il imprime toujours la même sortie à l'écran (par exempleHello World!
). En ce sens, n'est-ce pas pur? Non ce n'est pas. La raison pour laquelle ce n'est pas pur est parce que nous considérons l'impression de quelque chose à l'écran comme un effet secondaire. Si notre boîte noire produit des effets secondaires, elle n'est pas pure.Qu'est-ce qu'un effet secondaire? C’est là que le concept de transparence référentielle est utile. Si une fonction est référentiellement transparente, nous pouvons toujours remplacer les applications de cette fonction par leurs résultats. Notez que ce n'est pas la même chose que la fonction inline .
Dans la fonction inline, nous remplaçons les applications d'une fonction par le corps de la fonction sans altérer la sémantique du programme. Cependant, une fonction référentiellement transparente peut toujours être remplacée par sa valeur de retour sans altérer la sémantique du programme. Prenons l'exemple suivant.
Ici, nous avons souligné la définition de
greet
et cela n'a pas changé la sémantique du programme.Maintenant, considérez le programme suivant.
Ici, nous avons remplacé les applications du
greet
fonction par leurs valeurs de retour et cela a changé la sémantique du programme. Nous n'imprimons plus de salutations à l'écran. C'est la raison pour laquelle l'impression est considérée comme un effet secondaire, et c'est pourquoi lagreet
fonction est impure. Ce n'est pas référentiellement transparent.Maintenant, considérons un autre exemple. Considérez le programme suivant.
De toute évidence, la
main
fonction est impure. Cependant, latimeDiff
fonction est-elle pure ou impure? Bien que cela dépendeserverTime
ce qui provient d'un appel réseau impur, il est toujours référentiellement transparent car il renvoie les mêmes sorties pour les mêmes entrées et parce qu'il n'a aucun effet secondaire.zerkms sera probablement en désaccord avec moi sur ce point. Dans sa réponse , il a dit que la
dollarToEuro
fonction dans l'exemple suivant est impure parce que "cela dépend transitoirement de l'IO".Je dois être en désaccord avec lui parce que le fait que le
exchangeRate
provienne d'une base de données n'est pas pertinent. C'est un détail interne et notre méthodologie de boîte noire pour déterminer la pureté d'une fonction ne se soucie pas des détails internes.Dans les langages purement fonctionnels comme Haskell, nous avons une trappe d'échappement pour exécuter des effets d'E / S arbitraires. C'est appelé
unsafePerformIO
, et comme son nom l'indique, si vous ne l'utilisez pas correctement, ce n'est pas sûr, car cela pourrait briser la transparence référentielle. Cependant, si vous savez ce que vous faites, son utilisation est parfaitement sûre.Il est généralement utilisé pour charger des données à partir de fichiers de configuration au début du programme. Le chargement de données à partir de fichiers de configuration est une opération d'E / S impure. Cependant, nous ne voulons pas être gênés par le passage des données en entrée à chaque fonction. Par conséquent, si nous utilisons
unsafePerformIO
alors nous pouvons charger les données au niveau supérieur et toutes nos fonctions pures peuvent dépendre des données de configuration globale immuables.Notez que ce n'est pas parce qu'une fonction dépend de certaines données chargées à partir d'un fichier de configuration, d'une base de données ou d'un appel réseau que la fonction est impure.
Cependant, considérons votre exemple original qui a une sémantique différente.
Ici, je suppose que parce que
exchangeRate
n'est pas défini commeconst
, il va être modifié pendant l'exécution du programme. Si tel est le cas, alorsdollarToEuro
c'est définitivement une fonction impure parce que lorsque leexchangeRate
est modifié, il brisera la transparence référentielle.Cependant, si la
exchangeRate
variable n'est pas modifiée et ne sera jamais modifiée à l'avenir (c'est-à-dire si c'est une valeur constante), alors même si elle est définie commelet
, elle ne cassera pas la transparence référentielle. Dans ce cas,dollarToEuro
est en effet une fonction pure.Notez que la valeur de
exchangeRate
peut changer à chaque fois que vous exécutez à nouveau le programme et qu'elle ne cassera pas la transparence référentielle. Il ne rompt la transparence référentielle que s'il change pendant l'exécution du programme.Par exemple, si vous exécutez mon
timeDiff
exemple plusieurs fois, vous obtiendrez des valeurs différentesserverTime
et donc des résultats différents. Toutefois, comme la valeur deserverTime
ne change jamais pendant l'exécution du programme, latimeDiff
fonction est pure.la source
const
dans mon exemple.const
ladollarToEuro
fonction est en effet pure. La seule façon dont la valeur deexchangeRate
change serait si vous réexécutiez le programme. Dans ce cas, l'ancien processus et le nouveau processus sont différents. Par conséquent, il ne rompt pas la transparence référentielle. C'est comme appeler deux fois une fonction avec des arguments différents. Les arguments peuvent être différents mais dans la fonction, la valeur des arguments reste constante.eax
est effacé - via une charge ou un effacement - le code reste déterministe indépendamment de ce qui se passe d'autre et est donc pur. Sinon, réponse très complèteUne réponse d'un moi-puriste (où "moi" est littéralement moi, car je pense que cette question n'a pas un seul formel "bonne" réponse ):
Dans un langage dynamique tel que JS avec autant de possibilités pour modifier les types de base de patchs, ou créer des types personnalisés en utilisant des fonctionnalités telles que
Object.prototype.valueOf
il est impossible de dire si une fonction est pure juste en la regardant, car c'est à l'appelant de décider s'il veut pour produire des effets secondaires.Une démo:
Une réponse de moi-pragmatique:
De la définition même de wikipedia
En d'autres termes, il importe uniquement comment une fonction se comporte, pas comment elle est implémentée. Et tant qu'une fonction particulière détient ces 2 propriétés - elle est pure quelle que soit la façon dont elle a été implémentée.
Passons maintenant à votre fonction:
Il est impur car il ne qualifie pas l'exigence 2: il dépend de l'IO de manière transitoire.J'accepte que la déclaration ci-dessus soit erronée, voir l'autre réponse pour plus de détails: https://stackoverflow.com/a/58749249/251311
Autres ressources pertinentes:
la source
me
tant que zerkms qui fournit une réponse.add42
et le mienaddX
est purement que monx
peut être changé, et votreft
ne peut pas être changé (et donc,add42
la valeur de retour de ne varie pas en fonction deft
)?dollarToEuro
fonction dans votre exemple est impure. J'ai expliqué pourquoi je n'étais pas d'accord dans ma réponse. stackoverflow.com/a/58749249/783743Comme d'autres réponses l'ont dit, la façon dont vous avez mis en œuvre
dollarToEuro
,est en effet pur, car le taux de change n'est pas mis à jour pendant l'exécution du programme. Conceptuellement, cependant, il
dollarToEuro
semble que ce devrait être une fonction impure, dans la mesure où il utilise le taux de change le plus récent. La façon la plus simple d'expliquer cet écart est que vous n'avez pas implémentédollarToEuro
maisdollarToEuroAtInstantOfProgramStart
.La clé ici est qu'il existe plusieurs paramètres qui sont nécessaires pour calculer une conversion monétaire, et qu'une version vraiment pure du général
dollarToEuro
les fournirait tous. Les paramètres les plus directs sont le montant en USD à convertir et le taux de change. Cependant, comme vous souhaitez obtenir votre taux de change à partir des informations publiées, vous avez maintenant trois paramètres à fournir:L'autorité historique ici est votre base de données, et en supposant que la base de données n'est pas compromise, retournera toujours le même résultat pour le taux de change un jour particulier. Par conséquent, avec la combinaison de ces trois paramètres, vous pouvez écrire une version entièrement pure et autosuffisante du général
dollarToEuro
, qui pourrait ressembler à ceci:Votre implémentation capture des valeurs constantes pour l'autorité historique et la date de la transaction au moment où la fonction est créée - l'autorité historique est votre base de données et la date capturée est la date à laquelle vous démarrez le programme - tout ce qui reste est le montant en dollars , que l'appelant fournit. La version impure de
dollarToEuro
qui obtient toujours la valeur la plus à jour prend essentiellement le paramètre date implicitement, le définissant à l'instant où la fonction est appelée, ce qui n'est pas pur simplement parce que vous ne pouvez jamais appeler la fonction avec les mêmes paramètres deux fois.Si vous voulez en avoir une version pure
dollarToEuro
qui peut toujours obtenir la valeur la plus à jour, vous pouvez toujours lier l'autorité historique, mais laissez le paramètre date non lié et demandez la date à l'appelant comme argument, en finissant par avec quelque chose comme ça:la source
Je voudrais revenir un peu sur les détails spécifiques de JS et l'abstraction des définitions formelles, et parler des conditions à respecter pour permettre des optimisations spécifiques. C'est généralement la principale chose dont nous nous soucions lors de l'écriture de code (bien que cela aide également à prouver l'exactitude). La programmation fonctionnelle n'est ni un guide des dernières tendances ni un vœu monastique d'abnégation. C'est un outil pour résoudre des problèmes.
Lorsque vous avez un code comme celui-ci:
S'il
exchangeRate
n'a jamais pu être modifié entre les deux appels versdollarToEuro(100)
, il est possible de mémoriser le résultat du premier appeldollarToEuro(100)
et d'optimiser le deuxième appel. Le résultat sera le même, donc nous pouvons simplement nous souvenir de la valeur d'avant.Le
exchangeRate
peut être défini une fois, avant d'appeler une fonction qui le recherche, et jamais modifié. De manière moins restrictive, vous pouvez avoir un code qui recherche uneexchangeRate
fois une fonction ou un bloc de code particulier et utilise le même taux de change de manière cohérente dans cette étendue. Ou, si seulement ce fil peut modifier la base de données, vous pourriez supposer que si vous n'avez pas mis à jour le taux de change, personne d'autre ne l'a modifié sur vous.Si
fetchFromDatabase()
elle-même est une fonction pure évaluant une constante etexchangeRate
est immuable, nous pourrions plier cette constante tout au long du calcul. Un compilateur qui sait que c'est le cas pourrait faire la même déduction que vous avez fait dans le commentaire, qui estdollarToEuro(100)
évalué à 90,0, et remplacer l'expression entière par la constante 90,0.Cependant, s'il
fetchFromDatabase()
n'effectue pas d'E / S, ce qui est considéré comme un effet secondaire, son nom viole le principe du moindre étonnement.la source
Cette fonction n'est pas pure, elle s'appuie sur une variable extérieure, qui va presque certainement changer.
La fonction échoue donc au premier point que vous avez fait, elle ne retourne pas la même valeur quand pour les mêmes arguments.
Pour rendre cette fonction "pure", passez
exchangeRate
en argument.Cela satisferait alors les deux conditions.
Exemple de code:
la source
const
.Pour développer les points soulevés par d'autres sur la transparence référentielle: nous pouvons définir la pureté comme étant simplement la transparence référentielle des appels de fonction (c'est-à-dire que chaque appel à la fonction peut être remplacé par la valeur de retour sans changer la sémantique du programme).
Les deux propriétés que vous donnez sont toutes deux des conséquences de la transparence référentielle. Par exemple, la fonction suivante
f1
est impure, car elle ne donne pas le même résultat à chaque fois (la propriété que vous avez numérotée 1):Pourquoi est-il important d'obtenir le même résultat à chaque fois? Parce que l'obtention de résultats différents est un moyen pour un appel de fonction d'avoir une sémantique différente d'une valeur, et donc de rompre la transparence référentielle.
Disons que nous écrivons le code
f1("hello", "world")
, nous l'exécutons et obtenons la valeur de retour"hello"
. Si nous effectuons une recherche / remplacement de chaque appelf1("hello", "world")
et les remplaçons par,"hello"
nous aurons changé la sémantique du programme (tous les appels seront désormais remplacés par"hello"
, mais à l'origine environ la moitié d'entre eux auraient été évalués"world"
). Les appels àf1
ne sont donc pas référentiellement transparents, doncf1
impurs.Une autre façon dont un appel de fonction peut avoir une sémantique différente d'une valeur consiste à exécuter des instructions. Par exemple:
La valeur de retour de
f2("bar")
sera toujours"bar"
, mais la sémantique de la valeur"bar"
est différente de l'appelf2("bar")
puisque ce dernier se connectera également à la console. Remplacer l'un par l'autre changerait la sémantique du programme, donc il n'est pas référentiellement transparent, et doncf2
impur.Que votre
dollarToEuro
fonction soit référentiellement transparente (et donc pure) dépend de deux choses:exchangeRate
volonté changera jamais dans cette «portée»Il n'y a pas de «meilleur» champ d'application à utiliser; normalement, nous pensons à une seule exécution du programme ou à la durée de vie du projet. Par analogie, imaginez que les valeurs de retour de chaque fonction soient mises en cache (comme la table de mémo dans l'exemple donné par @ aadit-m-shah): quand aurions-nous besoin de vider le cache, pour garantir que les valeurs périmées n'interfèrent pas avec notre sémantique?
Si vous l'
exchangeRate
utilisiez,var
cela pourrait changer entre chaque appel àdollarToEuro
; nous aurions besoin d'effacer les résultats mis en cache entre chaque appel, il n'y aurait donc pas de transparence référentielle à proprement parler.En utilisant,
const
nous étendons la «portée» à une exécution du programme: il serait sûr de mettre en cache les valeurs de retourdollarToEuro
jusqu'à la fin du programme. Nous pourrions imaginer utiliser une macro (dans un langage comme Lisp) pour remplacer les appels de fonction par leurs valeurs de retour. Cette quantité de pureté est courante pour des éléments tels que les valeurs de configuration, les options de ligne de commande ou les ID uniques. Si nous nous limitons à penser à une exécution du programme, nous obtenons la plupart des avantages de la pureté, mais nous devons être prudents entre les exécutions (par exemple, enregistrer des données dans un fichier, puis les charger dans une autre exécution). Je n'appellerais pas de telles fonctions "pures" dans un sens abstrait (par exemple si j'écrivais une définition de dictionnaire), mais je n'ai aucun problème à les traiter comme pures dans leur contexte .Si nous considérons la durée de vie du projet comme notre «portée», nous sommes alors «le plus référentiellement transparent» et donc le «plus pur», même dans un sens abstrait. Nous n'aurions jamais besoin de vider notre cache hypothétique. Nous pourrions même faire cette "mise en cache" en réécrivant directement le code source sur le disque, pour remplacer les appels par leurs valeurs de retour. Cela pourrait même fonctionner sur plusieurs projets, par exemple, nous pourrions imaginer une base de données en ligne de fonctions et leurs valeurs de retour, où n'importe qui peut rechercher un appel de fonction et (s'il est dans la base de données) utiliser la valeur de retour fournie par quelqu'un de l'autre côté de la monde qui a utilisé une fonction identique il y a des années sur un projet différent.
la source
Comme écrit, c'est une fonction pure. Il ne produit aucun effet secondaire. La fonction a un paramètre formel, mais elle a deux entrées et produira toujours la même valeur pour deux entrées quelconques.
la source
Comme vous l'avez bien noté, "cela pourrait me donner un résultat différent demain" . Si tel était le cas, la réponse serait un "non" retentissant . Cela est particulièrement vrai si votre comportement souhaité
dollarToEuro
a été correctement interprété comme:Cependant, une interprétation différente existe, où elle serait considérée comme pure:
dollarToEuro
directement au-dessus est pur.Du point de vue de l'ingénierie logicielle, il est essentiel de déclarer la dépendance de
dollarToEuro
la fonctionfetchFromDatabase
. Par conséquent, remaniez la définition de cedollarToEuro
qui suit:Avec ce résultat, étant donné la prémisse qui
fetchFromDatabase
fonctionne de manière satisfaisante, alors nous pouvons conclure que la projection defetchFromDatabase
ondollarToEuro
doit être satisfaisante. Ou la déclaration "fetchFromDatabase
est pur" impliquedollarToEuro
est pur (carfetchFromDatabase
est une base pourdollarToEuro
par le facteur scalaire dex
.D'après le message d'origine, je peux comprendre que
fetchFromDatabase
c'est un temps de fonction. Améliorons l'effort de refactorisation pour rendre cette compréhension transparente, et donc clairement qualifiéefetchFromDatabase
de fonction pure:fetchFromDatabase = (horodatage) => {/ * voici l'implémentation * /};
En fin de compte, je voudrais refactoriser la fonctionnalité comme suit:
Par conséquent,
dollarToEuro
peut être testé unitaire en prouvant simplement qu'il appelle correctementfetchFromDatabase
(ou sa dérivéeexchangeRate
).la source
dollarToEuro
; Je l'ai mentionné dans le PO qu'il pourrait y avoir d'autres cas d'utilisation. J'ai choisi dollarToEuro parce qu'il évoque instantanément ce que j'essaie de faire, mais il pourrait y avoir quelque chose de moins subtil qui dépend d'une variable libre qui peut changer, mais pas nécessairement en fonction du temps. Dans cet esprit, je trouve que le refactorisé le plus voté est le plus accessible et celui qui peut aider les autres avec des cas d'utilisation similaires. Merci pour votre aide malgré tout.Je suis bilingue Haskell / JS et Haskell est l'une des langues qui fait beaucoup pour la pureté des fonctions, j'ai donc pensé vous donner la perspective de la façon dont Haskell le voit.
Comme d'autres l'ont dit, dans Haskell, la lecture d'une variable mutable est généralement considérée comme impure. Il y a une différence entre les variables et les définitions dans la mesure où les variables peuvent changer plus tard, les définitions sont les mêmes pour toujours. Donc, si vous l' aviez déclaré à ce moment-
const
là (en supposant qu'il ne s'agit que d'unnumber
et qu'il n'a pas de structure interne modifiable), lire à partir de cela serait utiliser une définition, qui est pure. Mais vous vouliez modéliser les taux de change évoluant avec le temps, et cela nécessite une sorte de mutabilité, puis vous entrez dans l'impureté.Pour décrire ce genre de choses impures (nous pouvons les appeler «effets» et leur utilisation «efficace» par opposition à «pur») dans Haskell, nous faisons ce que vous pourriez appeler la métaprogrammation . Aujourd'hui, la métaprogrammation fait généralement référence à des macros, ce qui n'est pas ce que je veux dire, mais plutôt l'idée d'écrire un programme pour écrire un autre programme en général.
Dans ce cas, en Haskell, nous écrivons un calcul pur qui calcule un programme efficace qui fera alors ce que nous voulons. Donc, tout l'intérêt d'un fichier source Haskell (au moins, celui qui décrit un programme, pas une bibliothèque) est de décrire un calcul pur pour un programme efficace qui produit-void, appelé
main
. Ensuite, le travail du compilateur Haskell consiste à prendre ce fichier source, à effectuer ce calcul pur et à placer ce programme efficace en tant qu'exécutable binaire quelque part sur votre disque dur pour l'exécuter plus tard à votre guise. Il y a un écart, en d'autres termes, entre le moment où le calcul pur s'exécute (tandis que le compilateur rend l'exécutable) et le moment où le programme efficace s'exécute (chaque fois que vous exécutez l'exécutable).Donc pour nous, les programmes efficaces sont vraiment une structure de données et ils ne font rien intrinsèquement juste en étant mentionnés (ils n'ont pas d'effets * secondaires- * en plus de leur valeur de retour; leur valeur de retour contient leurs effets). Pour un exemple très léger d'une classe TypeScript qui décrit des programmes immuables et certaines choses que vous pouvez faire avec eux,
La clé est que si vous en avez un,
Program<x>
aucun effet secondaire ne s'est produit et ce sont des entités totalement fonctionnellement pures. Le mappage d'une fonction sur un programme n'a aucun effet secondaire à moins que la fonction ne soit pas une fonction pure; séquencer deux programmes n'a pas d'effets secondaires; etc.Ainsi, par exemple, comment appliquer cela dans votre cas, vous pouvez écrire des fonctions pures qui renvoient des programmes pour obtenir des utilisateurs par ID et pour modifier une base de données et récupérer des données JSON, comme
puis vous pouvez décrire un travail cron pour boucler une URL et rechercher un employé et informer son superviseur d'une manière purement fonctionnelle comme
Le fait est que chaque fonction ici est une fonction complètement pure; rien ne s'est réellement passé jusqu'à ce que je le
action.run()
mette en marche. De plus, je peux écrire des fonctions comme,et si JS avait promis l'annulation, nous pourrions avoir deux programmes en course et prendre le premier résultat et annuler le second. (Je veux dire que nous pouvons toujours, mais il devient moins clair que faire.)
De même, dans votre cas, nous pouvons décrire l'évolution des taux de change avec
et
exchangeRate
pourrait être un programme qui examine une valeur mutable,mais même ainsi, cette fonction
dollarsToEuros
est maintenant une fonction pure d'un nombre à un programme qui produit un nombre, et vous pouvez raisonner à ce sujet de cette manière équationnelle déterministe que vous pouvez raisonner à propos de tout programme qui n'a pas d'effets secondaires.Le coût, bien sûr, c'est que vous devrez éventuellement appeler cela
.run()
quelque part , et ce sera impur. Mais toute la structure de votre calcul peut être décrite par un calcul pur, et vous pouvez pousser l'impureté aux marges de votre code.la source