Quels sont les avantages de la transparence référentielle pour un programmeur?


18

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).




3
La transparence référentielle vous permet d'utiliser le raisonnement équationnel pour: 1) prouver les propriétés du code et 2) écrire des programmes. Il y a quelques livres sur Haskell où les auteurs devraient comment vous pouvez commencer à partir de certaines équations que vous voulez qu'une fonction remplisse complètement et en utilisant un raisonnement juste équationnel, vous obtenez une implémentation de cette fonction, ce qui est donc certainement correct. Maintenant, combien cela peut être appliqué dans la programmation "au jour le jour" dépend probablement du contexte ...
Bakuriu

2
@err Aimez-vous le code plus facile à refactoriser parce que vous savez si appeler deux fois une fonction revient à stocker sa valeur dans une variable, puis à utiliser deux fois ladite variable? Diriez-vous que c'est un avantage pour votre programmation quotidienne?
Andres F.

L'avantage est que vous n'avez pas besoin de perdre du temps à penser à la non-transparence référentielle. Un peu comme les avantages des variables sont que vous n'avez pas besoin de perdre du temps à penser à l'allocation des registres.
user253751

Réponses:


37

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 computeProductPricemé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.

  • Vous pouvez même le mettre en cache et utiliser la version mise en cache.
  • Vous pouvez le rendre paresseux et reporter son appel lorsque vous en avez réellement besoin, sachant que la valeur ne changera pas entre-temps.
  • Vous pouvez appeler la méthode plusieurs fois, sachant qu'elle n'aura pas d'effets secondaires.
  • Vous pouvez raisonner sur la méthode elle-même dans un isolement du monde, sachant que tout ce dont elle a besoin sont les arguments.

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:

decimal math.parse(string t)

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 mathbibliothèque qui spécifie la précision des nombres à analyser. Donc, avec la valeur de 1, l'analyse de la chaîne "12.3456"donnera 12.3.

  • La culture, qui définit la mise en forme attendue. Par exemple, avec fr-FR, l'analyse "12.345"donnera 12345, 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 obtenir 5349une 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:

decimal math.parse(string t, base=10, precision=20, culture=cultures.en_us)

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.


4
Juste un addendum: la transparence référentielle s'applique à toutes les expressions dans une langue, pas seulement aux fonctions.
gardenhead

3
Notez qu'il existe des limites à la transparence de votre transparence. Rendre packet = socket.recv()référentiellement transparent défait plutôt le point de la fonction.
Mark

1
Doit être culture = cultures.invariant. À moins que vous ne vouliez créer accidentellement un logiciel qui ne fonctionne correctement qu'aux États-Unis.
user253751

@immibis: hm, bonne question. Quelles seraient les règles d'analyse invariant? Soit ils sont les mêmes que pour en_us, dans ce cas, pourquoi s'embêter, soit ils correspondent à un autre pays, dans quel cas, lequel et pourquoi celui-ci à la place en_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» entre 12,345.67et 12 345,67: toute «règle par défaut» fonctionnera pour quelques pays et ne fonctionnera pas pour d'autres.
Arseni Mourzenko

3
@ArseniMourzenko C'est généralement un "plus petit dénominateur commun" et similaire à la syntaxe utilisée par de nombreux langages de programmation (qui est également invariante à la culture). 12345analyse comme 12345, 12 345ou 12,345ou 12.345est une erreur. 12.345analysé 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.
user253751

11

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.


10
"Une fois que vous commencez à ajouter des variables de mutation, dont certaines ont au-delà d'une seule fonction, vous ne pouvez pas simplement lire le code, vous devez l'exécuter, dans votre tête ou dans le débogueur, pour savoir comment cela fonctionne vraiment. ": Bon point. En d'autres termes, la transparence référentielle ne signifie pas seulement qu'un morceau de code produira toujours le même résultat pour les mêmes entrées, mais aussi que le résultat produit est le seul effet de ce morceau de code, qu'il n'y a pas d'autre côté caché effet comme changer une variable qui a été définie très loin dans un autre module.
Giorgio

C'est un bon point. J'ai un petit problème avec l' argument le plus simple à lire / raisonner , car plus simple à lire ou à raisonner est un attribut de code quelque peu vague et subjectif.
Eyal Roth

Une fois que vous commencez à ajouter des variables mutantes, dont certaines ont une portée au-delà d'une seule fonction, mais pourquoi l'opération d'affectation est déconseillée même lorsque la portée de la variable est locale pour fonctionner?
rahulaga_dev

9

Les gens utilisent le terme «plus facile à raisonner», mais n'expliquent jamais ce que cela signifie. Prenons l'exemple suivant:

result1 = foo("bar", 12)
// 100 lines of code
result2 = foo("bar", 12)

Sont result1- ils result2identiques ou différents? Sans transparence référentielle, vous n'en avez aucune idée. Vous devez réellement lire le corps de foopour vous assurer, et éventuellement le corps des fooappels 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 result1mémoire en mémoire, car je ne sais pas si cela changerait. Ensuite, j'ai du code avec deux états: avant a result1é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.


1
Vous avez mentionné que la transparence référentielle vous permet de raisonner sur le résultat des appels à foo () et savoir si result1et result2sont les mêmes. Un autre aspect important est que s'il foo("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).
Giorgio

La seule "intégrité référentielle" que je connaisse concerne les bases de données relationnelles.
Mark

1
@Mark C'est une faute de frappe. Karl voulait parler de transparence référentielle, comme le montre clairement le reste de sa réponse.
Andres F.

6

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.