web-dev-qa-db-fra.com

Qu'est-ce qu'un système de type?

Contexte

Je conçois une langue, en tant que projet parallèle. J'ai un assembleur fonctionnel, un analyseur statique et une machine virtuelle pour cela. Comme je peux déjà compiler et exécuter des programmes non triviaux en utilisant l'infrastructure que j'ai construite, j'ai pensé faire une présentation à mon université.

Au cours de mon exposé, j'ai mentionné que le VM fournit un système de type, a été demandé "À quoi sert votre système de type?". Après avoir répondu, la personne s'est moquée de moi poser la question.

Ainsi, même si je vais presque certainement perdre la réputation de poser cette question, je me tourne vers les programmeurs.

Ma compréhension

Si je comprends bien, les systèmes de types sont utilisés pour fournir une couche supplémentaire d'informations sur les entités dans un programme, de sorte que le runtime, le compilateur ou toute autre machine, sache quoi faire avec les chaînes de bits sur lesquelles il opère. Ils aident également à maintenir les contrats - le compilateur (ou l'analyseur de code, ou l'exécution, ou tout autre programme) peut vérifier qu'à tout moment le programme fonctionne sur des valeurs que les programmeurs s'attendent à ce qu'il opère.

Les types peuvent également être utilisés pour fournir des informations à ces programmeurs humains. Par exemple, je trouve cette déclaration:

function sqrt(double n) -> double;

plus utile que celui-ci

sqrt(n)

Le premier donne beaucoup d'informations: que l'identifiant sqrt est une fonction, prend un seul double en entrée et produit un autre double en sortie. Ce dernier vous indique qu'il s'agit probablement d'une fonction prenant un seul paramètre.

Ma réponse

Donc, après avoir demandé "À quoi sert votre système de saisie?" J'ai répondu comme suit:

Le système de types est dynamique (les types sont affectés à des valeurs, pas à des variables qui les contiennent), mais solide sans règles de coercition surprenantes (vous ne pouvez pas ajouter de chaîne à un entier car ils représentent des types incompatibles, mais vous pouvez ajouter un entier à un nombre à virgule flottante) .

Le système de type est utilisé par le VM pour s'assurer que les opérandes des instructions sont valides; et peut être utilisé par les programmeurs pour s'assurer que les paramètres passés à leurs fonctions sont valides (c'est-à-dire de type correct).
Le système de types prend en charge le sous-typage et l'héritage multiple (les deux fonctionnalités sont disponibles pour les programmeurs), et les types sont pris en compte lorsque la répartition dynamique des méthodes sur les objets est utilisée - VM utilise des types pour vérifier par quelle fonction un message donné est-il implémenté pour un type donné.

La question de suivi était "Et comment le type est-il attribué à une valeur?". J'ai donc expliqué que toutes les valeurs sont encadrées et ont un pointeur pointant vers une structure de définition de type qui fournit des informations sur le nom du type, les messages auxquels il répond et les types dont il hérite.

Après cela, on s'est moqué de moi, et ma réponse a été rejetée avec le commentaire "Ce n'est pas un vrai système de type.".

Donc - si ce que j'ai décrit ne peut pas être qualifié de "vrai système de types", qu'est-ce qui le ferait? Cette personne avait-elle raison de dire que ce que je fournis ne peut pas être considéré comme un système de type?

51
Mael

Tout cela semble être une bonne description de ce que les systèmes de type fournissent. Et votre implémentation semble assez raisonnable pour ce qu'elle fait.

Pour certaines langues, vous n'aurez pas besoin des informations d'exécution car votre langue ne fait pas de distribution d'exécution (ou vous ne faites qu'une seule distribution via vtables ou un autre mécanisme, vous n'avez donc pas besoin des informations de type). Pour certaines langues, le simple fait d'avoir un symbole/espace réservé suffit car vous ne vous souciez que de l'égalité des types, pas de son nom ou de son héritage.

Selon votre environnement, la personne peut avoir voulu plus de formalisme dans votre système de typage. Ils veulent savoir ce que vous pouvez prouver avec lui, pas ce que les programmeurs peuvent faire avec ça. C'est assez courant dans le monde universitaire, malheureusement. Bien que les universitaires fassent de telles choses, car il est assez facile d'avoir des failles dans votre système de saisie qui permettent aux choses d'échapper à l'exactitude. Il est possible qu'ils en aient repéré un.

Si vous aviez d'autres questions, Types et langages de programmation est le livre canonique sur le sujet et peut vous aider à apprendre une partie de la rigueur requise par les universitaires, ainsi qu'une partie de la terminologie pour aider à décrire les choses.

31
Telastyn

J'aime la réponse de @ Telastyn en particulier à cause de la référence à l'intérêt académique pour le formalisme.

Permettez-moi d'ajouter à la discussion.

Qu'est-ce qu'un système de type?

Un système de type est un mécanisme permettant de définir, détecter et empêcher des états de programme illégaux. Il fonctionne en définissant et en appliquant des contraintes. Les définitions de contraintes sont types, et les applications de contraintes sont sages des types, par ex. dans la déclaration de variable.

Les définitions de type prennent généralement en charge les opérateurs de composition (par exemple, diverses formes de conjonction, comme dans les structures, le sous-classement et la disjonction, comme dans les énumérations, les unions).

Les contraintes, les usages des types, permettent parfois aussi des opérateurs de composition (par exemple au moins ceci, exactement ceci, ceci ou cela, ceci à condition que quelque chose d'autre tienne).

Si le système de type est disponible dans la langue et appliqué au moment de la compilation dans le but de pouvoir émettre des erreurs au moment de la compilation, il s'agit d'un système de type statique; cela empêche de nombreux programmes illégaux de se compiler et encore moins de s'exécuter.

(Un système de type statique empêche un programme de s'exécuter, qu'il soit connu (ou indécidable) que le programme atteindra jamais le code défectueux dont il se plaint. Un système de type statique détecte certains types de non-sens (violations des contraintes déclarées) et juge le programme par erreur avant qu'il ne s'exécute.)

Si un système de type est appliqué au moment de l'exécution, il s'agit d'un système de type dynamique qui empêche les états de programme illégaux: mais en arrêtant le programme en cours d'exécution, au lieu de l'empêcher de s'exécuter en premier lieu.

Une offre de système de type assez courante consiste à fournir des fonctionnalités statiques et dynamiques.

20
Erik Eidt

Oh mec, je suis ravi d'essayer de répondre à cette question du mieux que je peux. J'espère que je pourrai bien mettre mes pensées en ordre.

Comme l'a mentionné @Doval et le questionneur l'a souligné (quoique grossièrement), vous n'avez pas vraiment de système de type. Vous disposez d'un système de contrôles dynamiques utilisant des balises, qui est en général beaucoup plus faible, et aussi beaucoup moins intéressant.

La question de "qu'est-ce qu'un système de types" peut être assez philosophique, et nous pourrions remplir un livre avec différents points de vue sur la question. Cependant, comme il s'agit d'un site pour les programmeurs, je vais essayer de garder ma réponse aussi pratique que possible (et vraiment, les types sont extrêmement pratiques en programmation , malgré ce que certains peuvent penser).

Aperçu

Commençons par une compréhension du siège du pantalon de ce à quoi un système de type est bon, avant de plonger dans les fondements plus formels. n système de type impose une structure à nos programmes. Ils nous disent comment nous pouvons associer différentes fonctions et expressions. Sans structure, les programmes sont intenables et extrêmement complexes, prêts à nuire à la moindre erreur du programmeur.

Écrire des programmes avec un système de type, c'est comme conduire un soin à l'état neuf - les freins fonctionnent, les portes se ferment en toute sécurité, le moteur est huilé, etc. avec des spaghettis. Vous n'avez absolument aucun contrôle sur votre.

Pour fonder la discussion, disons que nous avons un langage avec l'expression littérale num[n] Et str[s] Qui représentent respectivement le chiffre n et la chaîne s et les fonctions primitives plus et concat, avec la signification voulue. De toute évidence, vous ne voulez pas pouvoir écrire quelque chose comme plus "hello" "world" Ou concat 2 4. Mais comment pouvons-nous empêcher cela? A priori , il n'y a pas de méthode pour distinguer le chiffre 2 de la chaîne littérale "world". Ce que nous voudrions dire, c'est que ces expressions devraient être utilisées dans différents contextes; ils ont différents types.

Langues et types

Revenons un peu en arrière: qu'est-ce qu'un langage de programmation? En général, nous pouvons diviser un langage de programmation en deux couches: la syntaxe et la sémantique. Celles-ci sont également appelées respectivement la statique et la dynamique . Il s'avère que le système de types est nécessaire pour arbitrer l'interaction entre ces deux parties.

Syntaxe

Un programme est un arbre. Ne vous laissez pas berner par les lignes de texte que vous écrivez sur un ordinateur; ce ne sont que les représentations lisibles par l'homme d'un programme. Le programme lui-même est un arbre de syntaxe abstraite . Par exemple, en C, nous pouvons écrire:

int square(int x) { 
    return x * x;
 }

C'est la syntaxe concrète du programme (fragment). La représentation arborescente est:

     function square
     /     |       \
   int   int x    return
                     |
                   times
                  /    \
                 x      x

Un langage de programmation fournit une grammaire définissant les arbres valides de ce langage ( une syntaxe concrète ou abstraite peut être utilisée). Cela se fait généralement en utilisant quelque chose comme la notation BNF. Je suppose que vous l'avez fait pour le langage que vous avez créé.

Sémantique

D'accord, nous savons ce qu'est un programme, mais ce n'est qu'une arborescence statique. Vraisemblablement, nous voulons que notre programme calcule réellement quelque chose. Nous avons besoin de sémantique.

La sémantique des langages de programmation est un domaine d'étude riche. D'une manière générale, il existe deux approches: sémantique dénotationnelle et sémantique opérationnelle . La sémantique dénotationnelle décrit un programme en le mappant dans une structure mathématique sous-jacente (par exemple, les nombres naturels, les fonctions continues, etc.). qui donne du sens à notre programme. La sémantique opérationnelle, au contraire, définit un programme en détaillant comment il s'exécute. À mon avis, la sémantique opérationnelle est plus intuitive pour les programmeurs (y compris moi-même), alors restons-en là.

Je ne vais pas expliquer comment définir une sémantique opérationnelle formelle (les détails sont un peu impliqués), mais en gros, nous voulons des règles comme les suivantes:

  1. num[n] Est une valeur
  2. str[s] Est une valeur
  3. Si num[n1] Et num[n2] Correspondent aux entiers n_1$ and $n_2$, then Plus (num [n1], num [n2]) `correspond à l'entier $ n_1 + n_2 $.
  4. Si str[s1] Et str[s2] Sont évalués sur les chaînes s1 et s2, alors concat(str[s1], str[s2]) est évalué sur la chaîne s1s2.

Etc. Les règles sont en pratique beaucoup plus formelles, mais vous obtenez l'essentiel. Cependant, nous rencontrons bientôt un problème. Que se passe-t-il lorsque nous écrivons ce qui suit:

concat(num[5], str[hello])

Hm. C'est une énigme. Nous n'avons défini aucune règle pour concaténer un nombre avec une chaîne. Nous pourrions essayer de créer une telle règle, mais nous savons intuitivement que cette opération n'a pas de sens. Nous ne voulons pas que ce programme soit valide. Et ainsi nous sommes inexorablement conduits aux types.

Les types

Un programme est un arbre tel que défini par la grammaire d'une langue. Les programmes ont un sens par les règles d'exécution. Mais certains programmes ne peuvent pas être exécutés; c'est-à-dire que certains programmes n'ont aucun sens . Ces programmes sont mal typés. Ainsi, la frappe caractérise des programmes significatifs dans une langue. Si un programme est bien typé, nous pouvons l'exécuter.

Donnons quelques exemples. Encore une fois, comme pour les règles d'évaluation, je présenterai les règles de frappe de manière informelle, mais elles peuvent être rendues rigoureuses. Voici quelques règles:

  1. Un jeton de la forme num[n] A le type nat.
  2. Un jeton de la forme str[s] A le type str.
  3. Si l'expression e1 A le type nat et l'expression e2 A le type nat, alors l'expression plus(e1, e2) a le type nat .
  4. Si l'expression e1 A le type str et l'expression e2 A le type str, alors l'expression concat(e1, e2) a le type str.

Ainsi, selon ces règles, il y a plus(num[5], num[2]) est de type nat, mais nous ne pouvons pas affecter un type à plus(num[5], str["hello"]). Nous disons qu'un programme (ou une expression) est bien typé si nous pouvons lui affecter n'importe quel type, et qu'il est mal typé sinon. Un système de type est son si tous les programmes bien typés peuvent être exécutés. Haskell est sain; C ne l'est pas.

Conclusion

Il existe d'autres vues sur les types. Les types correspondent dans un certain sens à la logique intuitionniste, et ils peuvent également être considérés comme des objets dans la théorie des catégories. Comprendre ces connexions est fascinant, mais il n'est pas essentiel si l'on veut simplement écrire ou même concevoir un langage de programmation. Cependant, la compréhension des types comme outil de contrôle des formations de programme est essentielle à la conception et au développement du langage de programmation. Je n'ai fait qu'effleurer la surface de ce que les types peuvent exprimer. J'espère que vous pensez qu'ils valent la peine d'être incorporés dans votre langue.

14
gardenhead