Quelle est la bonne explication du principe de correspondance de Tennent?


21

J'ai eu du mal à voir en quoi consiste ce principe et pourquoi est-il si important pour la conception du langage.

Fondamentalement, il indique que pour chaque expression exprdans le langage devrait être exactement la même que cette construction:

(function () { return expr; })()

En outre, j'ai entendu dire que Ruby obéit à ce principe, contrairement à Python. Je ne comprends pas pourquoi c'est vrai, ou si c'est vrai du tout.


3
Je ne vois pas pourquoi c'est hors sujet, quelqu'un pourrait-il me l'expliquer?
Andrew

3
Il y a quelques problèmes; Je l'ai retouché et je l'envoie aux programmeurs, où des discussions comme celle-ci sont un peu plus les bienvenues.
Déchiré

1
Ruby n'obéit pas à ce principe: Supposons qu'il exprobtienne la trace de pile actuelle.
Landei

Réponses:


18

Je n'ai jamais entendu parler auparavant du "principe de correspondance de Tennent" et encore moins de son importance dans la conception du langage. La recherche sur les expressions semble conduire à un blog Neal Gafter de 2006 qui définit ce qu'il pense que c'est et comment il pense que cela devrait également s'appliquer aux fermetures. Et la plupart de tous les autres dans les forums semblent faire référence à l'entrée de Gafter.

Voici cependant une mention dudit "TCP" par Douglas Crockford (un nom que je connais et auquel je fais confiance): http://java.sys-con.com/node/793338/ . En partie

Il y a certaines choses qui ne peuvent pas être enfermées de cette façon, telles que les déclarations de retour et les déclarations de rupture, qui, selon les partisans du principe de correspondance de Tennent (ou TCP), sont le symptôme d'une mauvaise odeur. Ouais! La conception du langage est déjà assez difficile sans avoir à faire face à des hallucinations olfactives. Donc, pour mieux comprendre le problème, j'ai acheté un exemplaire du livre de Tennent de 1981, Principes des langages de programmation.

Il s'avère que le principe de correspondance est descriptif et non normatif . Il l'utilise pour analyser le langage de programmation Pascal (aujourd'hui oublié), montrant une correspondance entre les définitions de variables et les paramètres de procédure. Tennent n'identifie pas le manque de correspondance des déclarations de retour comme un problème .

Il semble donc que le nom de «principe de correspondance de Tennent» soit mal utilisé, et tout ce dont Neal parle peut-être devrait être appelé «TCP imaginé et éventuellement généralisé de Gafter» ... ou quelque chose du genre. En tout cas, pas assez pour se cacher derrière un rideau de nom de livre épuisé


1
+1 pour "TCP imaginé et éventuellement généralisé de
Gafter

9

Je vois cela comme faisant partie d'une règle générale selon laquelle un langage bien conçu fait ce à quoi un programmeur devrait naturellement s'attendre. Si j'ai un bloc de code que je veux refactoriser dans une fermeture, et que j'enveloppe ce bloc avec la syntaxe appropriée sans vraiment penser aux lignes de code individuelles, alors je m'attends à ce que ce bloc fasse la même chose dans la fermeture qu'il a fait en ligne. Si certaines instructions utilisent le mot-clé "this" (peut-être implicitement) et que le langage fait que "this" utilisé à l'intérieur de la fermeture fait référence à une classe anonyme utilisée pour la représenter plutôt qu'à la classe qui définit la méthode qui définit la fermeture, alors la signification de ces déclarations ont changé, mon bloc de code ne fait plus ce que je pense, et je dois traquer un bogue et trouver comment changer mon code pour qu'il fonctionne pendant la fermeture.

Le problème pourrait également être atténué avec un IDE avec des outils de refactorisation intelligents, qui pourraient extraire les fermetures, détecter les problèmes potentiels et même ajuster automatiquement le code extrait pour résoudre les problèmes.


3

Claus Reinke: concernant la "Conception du langage basée sur les principes sémantiques" de Tennent
Donne une interprétation intéressante des principes:
"La correspondance est le principe qui nous permet de dire que

let(this=obj, x=5) { .. }  

et

((function(x) { .. }).call(obj,5))  

devrait être équivalent, et que tout ce que nous pouvons faire dans des listes de paramètres formelles, nous devrions également être en mesure de le faire dans des déclarations, et vice versa. "[Voir aussi, Reinke, ci-dessous.]

RD Tennent: Méthodes de conception de langage basées sur des principes sémantiques
"Deux méthodes de conception de langage basées sur des principes dérivés de l'approche dénotationnelle de la sémantique du langage de programmation sont décrites et illustrées par une application au langage Pascal. Les principes sont, premièrement, la correspondance entre paramétrique et mécanismes déclaratifs, et deuxièmement, un principe d'abstraction pour les langages de programmation adapté de la théorie des ensembles. Plusieurs extensions et généralisations utiles de Pascal émergent en appliquant ces principes, y compris une solution au problème des paramètres de tableau et une facilité de modularisation. "

Claus Reinke: "Sur la programmation fonctionnelle, la conception de langage et la persistance" sur Haskell


Claus Reinke: "Sur la programmation fonctionnelle, la conception du langage et la persistance" sur Haskell à community.haskell.org/~claus/publications/fpldp.html
Kris

2

Pour répondre à la question de savoir pourquoi le CP de Tennent est si important pour la conception de langage, je voudrais citer Neal Gafter :

Les principes de Tennent sont très puissants car les violations de ceux-ci ont tendance à apparaître dans la langue comme des défauts, des irrégularités, des restrictions inutiles, des interactions ou des complications inattendues, etc.

Toute violation de TCP est susceptible de nuire à certains programmeurs à l'avenir lorsqu'il s'attend à ce que les fermetures fonctionnent comme du code de non-fermeture, mais constate que, en violation de TCP, elles ne le font pas.


1

RE Python ne suit pas ce principe. Généralement, il suit le principe. Exemple de base:

>>> x = ['foo']
>>> x
['foo']
>>> x = (lambda: ['foo'])()
>>> x
['foo']

Cependant, Python définit les expressions et les instructions séparément. Étant donné que les ifbranches, les whileboucles, les affectations destructives et d'autres instructions ne peuvent pas du tout être utilisées dans les lambdaexpressions, la lettre du principe Tennent ne s'applique pas à elles. Même ainsi, se limiter à utiliser uniquement des expressions Python produit toujours un système complet de Turing. Je ne considère donc pas cela comme une violation du principe; ou plutôt, s'il viole le principe, aucun langage définissant séparément les déclarations et les expressions ne peut être conforme au principe.

De plus, si le corps de l' lambdaexpression capturait une trace de pile ou effectuait une autre introspection dans la machine virtuelle, cela pourrait provoquer des différences. Mais à mon avis, cela ne devrait pas être considéré comme une violation. Si expret (lambda: expr)() nécessairement compiler dans le même bytecode, alors le principe concerne vraiment les compilateurs et non la sémantique; mais s'ils peuvent être compilés en différents octets, nous ne devons pas nous attendre à ce que l'état de la machine virtuelle soit identique dans chaque cas.

Une surprise peut être rencontrée en utilisant la syntaxe de compréhension, bien que je pense que ce n'est pas non plus une violation du principe Tennent. Exemple:

>>> [x for x in xrange(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [f() for f in [lambda: x for x in xrange(10)]]  # surprise!
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
>>> # application of Tennent principle to first expression
... [(lambda: x)() for x in xrange(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [f() for f in [(lambda x: lambda: x)(x) for x in xrange(10)]]  # force-rebind x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> map(lambda f:f(), map(lambda x: lambda: x, xrange(10)))  # no issue with this form
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

La surprise est le résultat de la définition des compréhensions de liste. La compréhension «surprise» ci-dessus est équivalente à ce code:

>>> result = []
>>> for x in xrange(10):
...   # the same, mutable, variable x is used each time
...   result.append(lambda: x)
... 
>>> r2 = []
>>> for f in result:
...   r2.append(f())
... 
>>> r2
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]

Vu de cette façon, la compréhension «surprise» ci-dessus est moins surprenante et ne constitue pas une violation du principe Tennent.

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.