En programmation, quels sont les avantages de la transparence référentielle ?
La RT fait l'une des principales différences entre les paradigmes fonctionnels et impératifs, et est souvent utilisée par les partisans du paradigme fonctionnel comme un avantage clair sur l'impératif; mais dans tous leurs efforts, ces défenseurs n'expliquent jamais pourquoi c'est un avantage pour moi en tant que programmeur .
Bien sûr, ils auront leurs explications académiques sur la façon dont il est "pur" et "élégant", mais comment le rend-il meilleur qu'un code moins "pur"? En quoi cela me profite-t-il dans ma programmation quotidienne?
Remarque: il ne s'agit pas d'un doublon de Qu'est-ce que la transparence référentielle? Ce dernier aborde le sujet de ce qu'est la RT, tandis que cette question aborde ses avantages (qui ne sont peut-être pas si intuitifs).
la source
Réponses:
L'avantage est que les fonctions pures rendent votre code plus facile à raisonner. Ou, en d'autres termes, les effets secondaires augmentent la complexité de votre code.
Prenons un exemple de
computeProductPrice
méthode.Une méthode pure vous demanderait une quantité de produit, une devise, etc. Vous savez que chaque fois que la méthode est appelée avec les mêmes arguments, elle produira toujours le même résultat.
Une méthode non pure sera plus complexe à utiliser et à déboguer. Comme cela dépend de l'état des variables autres que les arguments et peut-être de les modifier, cela signifie qu'il pourrait produire des résultats différents lorsqu'il est appelé plusieurs fois, ou ne pas avoir le même comportement lorsqu'il n'est pas appelé du tout ou appelé trop tôt ou trop tard.
Exemple
Imaginez qu'il existe une méthode dans le cadre qui analyse un nombre:
Il n'a pas de transparence référentielle, car il dépend de:
La variable d'environnement qui spécifie le système de numérotation, c'est-à-dire Base 10 ou autre chose.
La variable dans la
math
bibliothèque qui spécifie la précision des nombres à analyser. Donc, avec la valeur de1
, l'analyse de la chaîne"12.3456"
donnera12.3
.La culture, qui définit la mise en forme attendue. Par exemple, avec
fr-FR
, l'analyse"12.345"
donnera12345
, car le caractère de séparation doit être,
, non.
Imaginez à quel point il serait facile ou difficile de travailler avec une telle méthode. Avec la même entrée, vous pouvez avoir des résultats radicalement différents selon le moment où vous appelez la méthode, car quelque chose, quelque part, a changé la variable d'environnement ou changé de culture ou défini une précision différente. Le caractère non déterministe de la méthode entraînerait plus de bugs et plus de cauchemar de débogage. Appeler
math.parse("12345")
et obtenir5349
une réponse car un code parallèle analysait des nombres octaux n'est pas bien.Comment réparer cette méthode évidemment cassée? En introduisant la transparence référentielle. En d'autres termes, en se débarrassant de l'état global et en déplaçant tout vers les paramètres de la méthode:
Maintenant que la méthode est pure, vous savez que peu importe quand vous appelez la méthode, elle produira toujours le même résultat pour les mêmes arguments.
la source
packet = socket.recv()
référentiellement transparent défait plutôt le point de la fonction.invariant
? Soit ils sont les mêmes que pouren_us
, dans ce cas, pourquoi s'embêter, soit ils correspondent à un autre pays, dans quel cas, lequel et pourquoi celui-ci à la placeen_us
, ou ils ont leurs règles spécifiques qui ne correspondent à aucun pays de toute façon , ce qui serait inutile. Il n'y a vraiment pas de «vraie réponse» entre12,345.67
et12 345,67
: toute «règle par défaut» fonctionnera pour quelques pays et ne fonctionnera pas pour d'autres.12345
analyse comme 12345,12 345
ou12,345
ou12.345
est une erreur.12.345
analysé comme un nombre à virgule flottante invariant donne toujours 12,345, conformément à la convention du langage de programmation d'utilisation. comme séparateur décimal. Les chaînes sont triées par leurs points de code Unicode et en respectant la casse. Etc.Ajoutez-vous souvent un point d'arrêt à un point de votre code et exécutez l'application dans le débogueur afin de déterminer ce qui se passe? Si vous le faites, c'est en grande partie parce que vous n'utilisez pas la transparence référentielle (RT) dans vos conceptions. Et il faut donc exécuter le code pour savoir ce qu'il fait.
L'intérêt de RT est que le code est hautement déterministe, c'est-à-dire que vous pouvez lire le code et déterminer ce qu'il fait, à chaque fois, pour le même ensemble d'entrées. Une fois que vous commencez à ajouter des variables mutantes, dont certaines ont une portée au-delà d'une seule fonction, vous ne pouvez pas simplement lire le code. Un tel code doit être exécuté, soit dans votre tête, soit dans le débogueur, pour savoir comment il fonctionne vraiment.
Plus le code est simple à lire et à raisonner, plus il est simple à maintenir et à repérer les bogues, de sorte qu'il économise du temps et de l'argent pour vous et votre employeur.
la source
Les gens utilisent le terme «plus facile à raisonner», mais n'expliquent jamais ce que cela signifie. Prenons l'exemple suivant:
Sont
result1
- ilsresult2
identiques ou différents? Sans transparence référentielle, vous n'en avez aucune idée. Vous devez réellement lire le corps defoo
pour vous assurer, et éventuellement le corps desfoo
appels de fonctions , et ainsi de suite.Les gens ne remarquent pas ce fardeau parce qu'ils y sont habitués, mais si vous allez travailler dans un environnement purement fonctionnel pendant un mois ou deux, vous reviendrez, vous le ressentirez et c'est énorme .
Il y a tellement de mécanismes de défense que les gens font pour contourner le manque de transparence référentielle. Pour mon petit exemple, je pourrais vouloir garder la
result1
mémoire en mémoire, car je ne sais pas si cela changerait. Ensuite, j'ai du code avec deux états: avant aresult1
été stocké et après. Avec la transparence référentielle, je peux simplement le recalculer facilement, tant que le recalcul ne prend pas beaucoup de temps.la source
result1
etresult2
sont les mêmes. Un autre aspect important est que s'ilfoo("bar", 12)
est référentiellement transparent, vous n'avez pas à vous demander si cet appel a produit des effets ailleurs (définir des variables? Supprimer un fichier? Que ce soit).Je dirais: la transparence référentielle n'est pas seulement bonne pour la programmation fonctionnelle mais pour tous ceux qui travaillent avec des fonctions car elle suit le principe du moindre étonnement.
Vous avez une fonction et pouvez mieux raisonner sur ce qu'elle fait car il n'y a pas de facteurs externes que vous devez prendre en compte, pour une entrée donnée, la sortie sera toujours la même. Même dans mon langage impératif, j'essaie de suivre ce paradigme autant que possible, la prochaine chose qui en découle automatiquement est: de petites fonctions faciles à comprendre au lieu des horribles 1000 fonctions de ligne que je rencontre parfois.
Ces grandes fonctions font de la magie et j'ai peur de les toucher car elles peuvent se briser de manière spectaculaire.
Les fonctions pures ne sont donc pas uniquement destinées à la programmation fonctionnelle, mais à chaque programme.
la source