Je programme depuis plusieurs années, mais je ne connais pas très bien le CS théorique. J'ai récemment essayé d'étudier les langages de programmation, et dans le cadre de cela, la vérification de type et l'inférence.
Ma question est, si j'essaie d'écrire un programme d'inférence de type et de vérification d'un langage de programmation, et que je souhaite prouver que mon vérificateur de typage fonctionne, quelle est exactement la preuve que je recherche?
En langage clair, je veux que mon vérificateur de type puisse identifier les erreurs dans un morceau de code qui pourraient se produire au moment de l'exécution. Si je devais utiliser quelque chose comme Coq pour essayer de prouver que mon implémentation est correcte, qu'est-ce que cette "preuve d'exactitude" tentera de montrer?
la source
2 + "hello"
celui-ci sont «bloqués»). Une fois ceci formalisé, vous pouvez alors prouver le théorème de la solidité du type. Cela signifie qu'aucun programme typable ne peut jamais évoluer vers une erreur de type immédiate. Formellement, vous prouvez que si un programme est typable, et pour tout : si exécute étapes pour devenir , alors n'a pas d'erreur de type immédiate. (1/2)n ≥ M n N NRéponses:
La question peut être interprétée de deux manières:
Le premier est vraiment une question de vérification de programme et n'a pas grand-chose à voir avec la saisie. Il suffit de montrer que votre implémentation répond à ses spécifications, voir la réponse d'Andrej.
Permettez-moi de parler de la dernière question. Comme Andrej l'a dit, d'un point de vue abstrait, un système de typage semble imposer des propriétés aux programmes. En pratique, votre système de dactylographie cherche à empêcher les erreurs de se produire, ce qui signifie que les programmes typables ne doivent pas présenter la classe des erreurs d'intérêt. Afin de montrer que fait ce que vous pensez qu'il devrait, vous devez faire deux choses.TT T
Tout d'abord, vous définissez formellement ce que signifie pour un programme une erreur de frappe immédiate . Il y a plusieurs façons de définir cela - c'est à vous de décider. En règle générale, nous voulons empêcher des programmes comme
2 + "hello"
. En d'autres termes, vous devez définir un sous-ensemble de programmes, appelé Bad , qui contient exactement les programmes avec une erreur de frappe immédiate.Vous devez ensuite prouver que les programmes typables ne peuvent jamais évoluer en programmes en Bad . Formalisons cela. Laissez votre jugement de frappe être Rappelons que cela doit être lu comme suit: le programme a le type , en supposant que les variables libres sont typées comme donné par l'environnement . Alors le théorème que vous voulez prouver est:M α ΓΓ⊢M:α. M α Γ
Théorème. Chaque fois que et puis Bad .M → ⋯ → N N ∉Γ⊢M:α M→⋯→N N∉
Comment prouver ce théorème dépend des détails de la langue, du système de frappe et de votre choix de Bad .
Un moyen standard de définir Bad est - à - dire: un terme a une erreur de type immédiat si elle est ni une valeur , ni a une étape de réduction . (Dans ce cas, est souvent appelé bloqué .) Cela ne fonctionne que pour la sémantique opérationnelle à petite étape. Une façon standard de prouver le théorème est de montrer queM → N MM M→N M
M → N Γ ⊢ N : αΓ⊢M:α et impliquent ensemble . C'est ce qu'on appelle la "réduction du sujet". Elle est généralement prouvée par une induction simultanée sur la dérivation du jugement de typage et la longueur des réductions.M→N Γ⊢N:α
Chaque fois que alors n'est pas en mauvais état . Ceci est généralement également prouvé par induction sur la dérivation du jugement de typage.MΓ⊢M:α M
Notez que tous les systèmes de frappe n'ont pas de "réduction de sujet", par exemple des types de session. Dans ce cas, des techniques de preuve plus sophistiquées sont nécessaires.
la source
C'est une bonne question! Il demande ce que nous attendons des types dans un langage tapé.
Notez d'abord que nous pouvons taper n'importe quel langage de programmation avec l' unité : il suffit de choisir une lettre, de dire
U
et de dire que chaque programme a du typeU
. Ce n'est pas terriblement utile, mais cela fait un point.Il existe de nombreuses façons de comprendre les types, mais du point de vue du programmeur, je pense que ce qui suit est utile. Considérez un type comme une spécification ou une garantie . Dire que a le type revient à dire que "nous garantissons / attendons / demandons que satisfasse la propriété codée par ". Souvent, est quelque chose de simple , auquel cas la propriété est simplement "c'est un entier".A e A Ae A e A A
int
Il n'y a pas de fin à la façon dont vos types peuvent être expressifs. En principe, il peut s'agir de n'importe quel type d'énoncés logiques, ils peuvent utiliser la théorie des catégories, etc. Vous pouvez aller plus loin, en ce moment j'écoute une conférence sur les "logiques de séparation simultanées" qui vous permet de parler du fonctionnement des programmes simultanés avec un état partagé. Des trucs de fantaisie.
L'art des types dans la conception de langages de programmation est un équilibre entre expressivité et simplicité :
La simplicité ne doit pas être sous-estimée, car tous les programmeurs n'ont pas de doctorat en théorie des langages de programmation.
Revenons donc à votre question: comment savez-vous que votre système de typage est bon ? Eh bien, prouvez des théorèmes qui montrent que vos types sont équilibrés. Il y aura deux types de théorèmes:
Théorèmes qui disent que vos types sont utiles . Savoir qu'un programme a un type devrait impliquer certaines garanties, par exemple que le programme ne restera pas bloqué (ce serait un théorème de sécurité ). Une autre famille de théorèmes relierait les types à des modèles sémantiques afin que nous puissions commencer à utiliser de vrais mathématiques pour prouver des choses sur nos programmes (ce seraient des théorèmes d'adéquation , et bien d'autres). L'unité ci-dessus est mauvaise car elle n'a pas de théorèmes aussi utiles.
Théorèmes qui disent que vos types sont simples . Un élément fondamental serait qu'il est possible de déterminer si une expression donnée a un type donné. Une autre fonctionnalité de simplicité consiste à fournir un algorithme pour déduire un type. D'autres théorèmes sur la simplicité seraient: qu'une expression a au plus un type, ou qu'une expression a un type principal (c'est-à-dire, le "meilleur" parmi tous les types qu'elle a).
Il est difficile d'être plus précis car les types sont un mécanisme très général. Mais j'espère que vous voyez ce que vous devez viser. Comme la plupart des aspects de la conception d'un langage de programmation, il n'y a pas de mesure absolue de réussite. Au lieu de cela, il y a un espace de possibilités de conception, et l'important est de comprendre où dans l'espace vous êtes, ou voulez être.
la source
Il y a quelques choses différentes que vous pourriez dire par "prouver que mon vérificateur de typage fonctionne". Ce qui, je suppose, fait partie de ce que demande votre question;)
La moitié de cette question prouve que votre théorie des types est suffisamment bonne pour prouver les propriétés du langage. La réponse d'Andrej aborde très bien ce domaine imo. L'autre moitié de la question est - en supposant que la langue et son système de type sont déjà fixes - comment pouvez-vous prouver que votre vérificateur de type particulier met en œuvre correctement le système de type? Il y a deux perspectives principales que je peux voir ici.
La première est: comment pouvons-nous jamais croire qu'une implémentation particulière correspond à ses spécifications? Selon le degré d'assurance que vous souhaitez, vous pouvez être satisfait d'une grande suite de tests, ou vous pouvez vouloir une sorte de vérification formelle, ou plus probablement un mélange des deux . L'avantage de cette perspective est qu'elle met vraiment en évidence l'importance de fixer des limites aux revendications que vous faites: que signifie exactement «corriger»? quelle partie du code est vérifiée, par rapport à quelle partie est le TCB supposé correct? etc. L'inconvénient est que trop réfléchir à cela mène à des trous de lapin philosophiques - enfin, "inconvénient" si vous n'aimez pas ces trous de lapin.
La deuxième perspective est une interprétation plus mathématique de l'exactitude. Lorsque nous traitons des langues en mathématiques, nous mettons souvent en place des "modèles" pour nos "théories" (ou vice versa), puis essayons de prouver: (a) tout ce que nous pouvons faire dans la théorie, nous pouvons le faire dans le modèle, et (b) tout ce que nous pouvons faire dans le modèle, nous pouvons le faire dans la théorie. (Ce sont la solidité et l' exhaustivitéthéorèmes. Lequel qui dépend de si vous "avez commencé" à partir de la théorie syntaxique ou du modèle sémantique.) Avec cet état d'esprit, nous pouvons penser que votre implémentation de vérification de type est un modèle particulier pour la théorie de type en question. Donc, vous voudriez prouver cette correspondance bidirectionnelle entre ce que votre implémentation peut faire et ce que la théorie dit que vous devriez pouvoir faire. L'avantage de cette perspective est qu'elle se concentre vraiment sur la question de savoir si vous avez couvert tous les cas d'angle, si votre implémentation est terminée dans le sens de ne pas laisser de programmes qu'elle devrait accepter comme de type sécurisé et si votre implémentation est solide dans le sentiment de ne laisser entrer aucun programme qu'il devrait rejeter comme mal typé. L'inconvénient est que votre preuve de correspondance est susceptible d'être assez séparée de la mise en œuvre elle-même,
la source