«Une preuve est un programme. la formule prouvée est un type pour le programme ”

37

C’est peut-être une question philosophique, mais je pense qu’il ya une réponse objective à cette question.

Si vous lisez l'article de Wikipédia sur Haskell, vous trouverez ce qui suit:

La langue est enracinée dans les observations de Haskell Curry et de ses descendants intellectuels, selon lesquelles "une preuve est un programme; la formule prouvée est un type pour le programme"

Maintenant, ce que je demande, c'est: cela ne s'applique-t-il pas à peu près à tous les langages de programmation? Quelle fonctionnalité (ou ensemble de fonctionnalités) de Haskell le rend conforme à cette déclaration? En d’autres termes, quels sont les effets remarquables de cette affirmation sur la conception de la langue?


la source
4
Quelqu'un veut expliquer pourquoi les votes "rapprochés", s'il vous plaît?
1
@Grigory Javadyan: Je n'ai pas voté pour la clôture, mais c'est probablement parce que la question est hors-sujet pour SO - les questions philosophiques, objectivement responsables ou non, ne sont généralement pas appropriées ici. Dans ce cas, cependant, je pense que cela est justifiable, car la réponse a de profondes implications pratiques sur la manière dont Haskell est réellement utilisé.
2
@Grigory: si cette question posait problème avec une solution réelle (en code), elle pouvait rester sur SO. Voté pour fermer et passer aux programmeurs.
9
J'ajouterai à cela parce que je suis un peu à la vapeur - les réponses à cette question sont légion: de nombreuses références à la recherche scientifique en informatique et, dans ce sens, plus "objectives" que 90% des SO. De plus, les critères de sixlettervariable (que la solution a besoin de code) sont incroyablement restrictifs pour un large éventail de véritables questions de programmation qui ne sont ni subjectives ni hors sujet. Je ne voudrais vraiment pas que le débat inclusionniste / délétionniste revienne sur SO, mais si les fils de programmation comme celui-ci sont clairement
ébranlés
2
Je suis ambivalent quant au résultat final, principalement parce que je ne sais vraiment pas quel type de contenu est censé être sur Programmers.SE ou SO. Mais je dirai que les programmeurs sont décrits à plusieurs endroits comme étant des "questions subjectives", ce que cette question n’est absolument pas . Ma réponse est à peu près aussi informelle et vague que possible, et je pourrais tout de même sauvegarder la majeure partie facilement avec des références acceptées même par les rédacteurs de Wikipédia.
CA McCann

Réponses:

38

Le concept essentiel s’applique universellement d’une certaine manière, oui, mais rarement de manière utile.

Pour commencer, du point de vue de la théorie des types, les langages "dynamiques" sont considérés comme ayant un seul type, qui contient (entre autres) des métadonnées sur la nature de la valeur vue par le programmeur, y compris ce que ces langages dynamiques appellent un "type" eux-mêmes (ce qui n'est pas la même chose, conceptuellement). De telles preuves étant susceptibles de ne pas être intéressantes, ce concept concerne principalement les langages utilisant des systèmes de types statiques.

En outre, de nombreuses langues supposées avoir un "système de types statiques" doivent être considérées comme dynamiques dans la pratique, dans ce contexte, car elles permettent l'inspection et la conversion de types au moment de l'exécution. En particulier, cela signifie toute langue avec un support intégré par défaut pour la "réflexion" ou autre. C #, par exemple.

Haskell présente une quantité inhabituelle d'informations attendues par un type. En particulier, les fonctions ne peuvent dépendre d'aucune valeur autre que celles spécifiées comme arguments. En revanche, dans une langue avec des variables globales modifiables, toute fonction peut (potentiellement) inspecter ces valeurs et modifier le comportement en conséquence. Ainsi, une fonction de Haskell avec type A -> Bpeut être considérée comme un programme miniature prouvant que cela Aimplique B; une fonction équivalente dans de nombreuses autres langues ne ferait que nous dire que A, quel que soit l'état global combiné, cela implique B.

Notez que bien que Haskell prenne en charge des éléments tels que les types de réflexion et les types dynamiques, l' utilisation de telles fonctionnalités doit être indiquée dans la signature de type d'une fonction; de même pour l'utilisation de l'état global. Ni est disponible par défaut.

Il existe également des moyens de casser des choses dans Haskell, par exemple en autorisant des exceptions d'exécution ou en utilisant des opérations primitives non standard fournies par le compilateur, mais il existe de fortes espérances sur le fait qu'elles ne seront utilisées que si elles sont parfaitement comprises ». t endommager le sens du code externe. En théorie, on pourrait en dire autant des autres langues, mais dans la plupart des autres langues, il est plus difficile de faire des choses sans "tricher" et moins mal vu de "tricher". Et bien sûr, dans les vrais langages "dynamiques", le tout reste sans importance.

Le concept peut également être poussé beaucoup plus loin qu’en Haskell.

CA McCann
la source
Notez cependant que les exceptions peuvent être entièrement intégrées dans un système de types.
Gardenhead
18

Vous avez raison de dire que la correspondance entre Curry et Howard est très générale. Il est intéressant de se familiariser avec son histoire: http://en.wikipedia.org/wiki/Curry-Howard_correspondence

Vous noterez que, telle que formulée à l'origine, cette correspondance s'appliquait particulièrement à la logique intuitionniste d'un côté et au calcul lambda simplement typé (STLC) de l'autre.

Haskell classique - soit les versions de 1998 ou même antérieures, était très proche du STLC et, pour l’essentiel, il existait une traduction très simple et directe entre toute expression donnée en Haskell et un terme correspondant dans le STLC (avec récursion et quelques types primitifs). Cela a donc rendu Curry-Howard très explicite. Aujourd'hui, grâce aux extensions, une telle traduction est un peu plus complexe.

Donc, dans un sens, la question est de savoir pourquoi Haskell "se désargue" dans le STLC de manière aussi simple. Deux choses viennent à l'esprit:

  • Les types. Contrairement à Scheme, qui est aussi une sorte de calcul lambda sucré (entre autres), Haskell est fortement typé. Cela signifie qu'il n'existe pas de termes classiques dans Haskell qui, par définition, ne peuvent pas être bien typés dans le STLC.
  • Pureté. Encore une fois, contrairement à Scheme, mais comme le STLC, Haskell est un langage pur, référentiel transparent. C'est assez important. Les langues avec effets secondaires peuvent être intégrées dans des langues sans effets secondaires. Cependant, cela représente une transformation du programme dans son ensemble, pas simplement un désir local. Donc, pour avoir la correspondance directe , il est nécessaire de commencer par un langage purement fonctionnel.

Haskell, comme la plupart des langues, échoue également en ce qui concerne l’application directe de la correspondance Curry-Howard. Haskell, en tant que langage complet, contient la possibilité d'une récursion illimitée, et donc de non-résiliation. Le STLC n'a pas d'opérateur de point fixe, n'est pas complet et se normalise fortement , ce qui signifie qu'aucune réduction d'un terme dans le STLC n'aboutira. La possibilité de récursivité signifie que l'on peut "tricher" Curry-Howard. Par exemple, let x = x in xa le typeforall a. a- c’est-à-dire qu’il ne revient jamais, je peux prétendre que cela me donne quelque chose! Comme nous pouvons toujours le faire en Haskell, cela signifie que nous ne pouvons pas "croire" pleinement en une preuve correspondant à un programme Haskell, à moins de disposer d'une preuve distincte de la fin du programme.

La lignée de programmation fonctionnelle antérieure à Haskell (notamment la famille ML) résultait de la recherche en CS centrée sur la construction de langages sur lesquels vous pouviez facilement prouver des choses (entre autres), recherche très consciente et issue de la théorie de l’hébergement. Inversement, Haskell a servi à la fois de langage hôte et d'inspiration à un certain nombre d'assistants de preuve en cours de développement, tels que Agda et Epigram, qui sont enracinés dans des développements de la théorie des types très liés à la lignée de CH.

sclv
la source
1
Il serait peut-être bon de souligner que la non-conclusion finit par saper la preuve, bien que catastrophique d’un point de vue logique, de préserver de nombreuses autres propriétés. En particulier, une fonction A -> B, à partir d'un A, produira un Bou rien du tout. Il ne produira jamais de type C, et quelle que soit la valeur du type Bfourni ou dépend toujours exclusivement du type Afourni.
@camccann - un peu piquant, mais je ferais la distinction entre le bas et "rien du tout", ce qui est plutôt du genre Voidnon? La paresse rend les choses plus compliquées et moins compliquées. Je dirais qu'une fonction de produit A -> B toujours une valeur de type B, mais cette valeur peut avoir moins d'informations que ce à quoi on pourrait s'attendre.
sclv
Nitpicking est amusant! Quand je dis "rien", je veux dire au niveau de la valeur dans un contexte d’évaluation, alors que bottom n’existe vraiment qu’en tant qu’abstraction, et non pas comme une chose tangible. Une expression en cours d'évaluation ne "verra" jamais une valeur de bas, mais uniquement les termes qu'elle n'utilise pas (ce qui pourrait être le fond) et les termes qu'elle utilise (qui ont des valeurs non inférieures). Essayer d'utiliser "bottom" ne se produit jamais "dans un sens", car cela met fin à l'évaluation de l'expression entière avant que l'utilisation ne se produise.
12

Au premier ordre, la plupart des autres langues (faiblement et / ou uni-typées) ne prennent pas en charge la délimitation stricte au niveau de la langue entre un

  • proposition (c.-à-d. un type)
  • une preuve (c'est-à-dire un programme montrant comment nous pouvons construire la proposition à partir d'un ensemble de primitives et / ou d'autres constructions d' ordre supérieur )

et la relation stricte entre les deux. Au mieux, les meilleures garanties offertes par d’autres langues sont:

  • compte tenu d'une contrainte limitée sur l'entrée, ainsi que de tout ce qui se passe dans l'environnement à ce moment-là, nous pouvons produire une valeur avec une contrainte limitée. (types statiques traditionnels, cf C / Java)
  • chaque construction est du même type (types dynamiques, cf ruby ​​/ python)

Notez que par type , nous nous référons à une proposition , et donc à quelque chose qui décrit beaucoup plus d’informations que simplement int ou bool . En Haskell, il y a une culture qui pénètre d' une fonction qui n'est affectée que par ses arguments - sans exception *.

Pour être un peu plus rigoureux, l’idée générale est qu’en imposant une approche intuitionniste rigide à (presque) toutes les constructions de programme (c’est-à-dire que nous ne pouvons que prouver ce que nous pouvons construire), et en limitant l’ensemble des constructions primitives dans un tel environnement. façon dont nous avons

  • propositions strictes pour toutes les primitives de langage
  • un ensemble limité de mécanismes par lesquels les primitives peuvent être combinées

Les constructions Haskell ont tendance à très bien se prêter à la réflexion sur leur comportement. Si nous pouvons construire une preuve (lire: fonction) prouvant Aimplique B, c'est a très propriétés utiles:

  • ça tient toujours (tant qu'on a un A, on peut en construire un B)
  • cette implication ne repose que sur A, et rien d' autre.

nous permettant ainsi de raisonner efficacement sur les invariants locaux / globaux. Pour revenir à la question initiale. Les fonctionnalités linguistiques de Haskell qui facilitent le mieux cet état d'esprit sont les suivantes:

  • Pureté / Segmentation des effets en constructions explicites (les effets sont à la fois comptabilisés et typés!)
  • Type Inference / Vérification dans les compilateurs Haskell
  • La possibilité d'incorporer des invariants de contrôle et / ou de flux de données dans les propositions / types qu'un programme se propose de prouver: (avec polymorphisme, familles de types, GADT, etc.)
  • Intégrité référentielle

Aucune d'entre elles n'est unique à Haskell (beaucoup de ces idées sont incroyablement anciennes). Cependant, lorsqu'il est combiné avec un riche ensemble d'abstractions dans les bibliothèques standards (habituellement trouvés dans les classes de type), divers sucrage niveau de syntaxe et d' un strict engagement à la pureté dans la conception du programme, nous nous retrouvons avec une langue qui réussit à être à la fois Pratique pour les applications du monde réel , mais en même temps, il est plus facile de raisonner sur les langues les plus traditionnelles.

Cette question mérite une réponse suffisamment profonde et je ne pourrais pas lui rendre justice dans ce contexte. Je suggérerais d'en lire plus sur wikipedia / dans la littérature:

* NB: Je passe sous silence / ignore certains des aspects les plus délicats des impuretés de Haskell (exceptions, non-résiliation, etc.) qui ne feraient que complexifier l'argument.

Raeez
la source
4

Quelle caractéristique? Le système de types (statique, pur, polymorphe). Les "théorèmes gratuits" de Wadler constituent un bon point de départ. Effet notable sur la conception de la langue? Le type IO, les classes de type.

ja.
la source
0

La hiérarchie de Kleene nous montre que les preuves ne sont pas des programmes.

La première relation récursive est soit:

R1( Program , Iteration )  Program halts at Iteration.
R2( Theorem , Proof ) Proof proves a Theorem.

Les premières relations énumérables récursivement sont:

(exists x) R1( Program , x )  Program Halts.
(exists x) R2( Theorem , x)   Theorem is provable.

Ainsi, un programme est un théorème et l'itération à laquelle il s'arrête ressemble à la preuve qui prouve le théorème.

Program = Theorem
Iteration = Proof

Lorsqu'un programme est correctement produit à partir d'une spécification, nous devons être en mesure de prouver qu'il satisfait à la spécification, et si nous pouvons prouver qu'un programme satisfait à une spécification, la synthèse de programme est correcte. Nous effectuons donc la synthèse du programme si et seulement si nous prouvons que le programme satisfait aux spécifications. Le théorème selon lequel le programme satisfait à la spécification est le programme en ce sens qu'il fait référence au programme synthétisé.

Les fausses conclusions de Martin Lof n'ont jamais produit de programme informatique et il est étonnant que les gens croient qu'il s'agit d'une méthodologie de synthèse de programme. Aucun exemple complet d'un programme en cours de synthèse n'a jamais été donné. Une spécification telle que "entrer un type et sortir un programme de ce type" n'est pas une fonction. Il existe plusieurs programmes de ce type et choisir l'un au hasard n'est pas une fonction récursive ni même une fonction. C'est juste une tentative stupide de montrer la synthèse de programme avec un programme stupide qui ne représente pas un vrai programme informatique calculant une fonction récursive.

Charlie Volkstorf
la source
2
comment cela répond-il à la question posée, "quels sont les effets notables de cette affirmation sur la conception de la langue?"
gnat
1
@gnat - cette réponse répond à une hypothèse sous-jacente à la question initiale, à savoir: " doesn't this really apply to pretty much all the programming languages?" Cette réponse prétend / montre que cette hypothèse est invalide, il est donc insensé de traiter le reste des questions basées sur une prémisse erronée. .