Oh mec, je suis excité d'essayer de répondre à cette question du mieux que je peux. J'espère que je peux mettre mes pensées en ordre correctement.
Comme @Doval et le questionneur l'ont fait remarquer (bien que de manière grossière), vous n'avez pas vraiment de système de type. Vous avez 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 le sujet. Cependant, comme il s’agit d’un site pour les programmeurs, je vais essayer de garder ma réponse aussi pratique que possible (et en réalité, les types sont extrêmement pratiques en programmation, malgré ce que certains pourraient penser).
Vue d'ensemble
Commençons par un siège-du-pantalon et comprenons à quoi sert un système de types, avant de plonger dans les bases plus formelles. Un système de types 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 à causer des dommages à la moindre erreur du programmeur.
Écrire des programmes avec un système de types revient à conduire prudemment à l’état neuf: les freins fonctionnent, les portes se ferment en toute sécurité, le moteur est huilé, etc. en spaghettis. Vous n'avez absolument aucun contrôle sur votre.
Pour fonder la discussion, disons que nous avons un langage avec une expression littérale num[n]
et str[s]
qui représente le nombre n et la chaîne s, respectivement, et des fonctions primitives plus
et concat
, avec le sens voulu. Clairement, 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’existe aucune méthode permettant de distinguer le chiffre 2 du littéral de chaîne "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, on peut diviser un langage de programmation en deux couches: la syntaxe et la sémantique. Celles-ci sont également appelées la statique et la dynamique , respectivement. Il s'avère que le système de types est nécessaire pour assurer la médiation de 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 un programme. Le programme lui-même est un arbre de syntaxe abstraite . Par exemple, en C, nous pourrions écrire:
int square(int x) {
return x * x;
}
C'est la syntaxe concrète du programme (fragment). L'arborescence 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). Ceci est généralement fait en utilisant quelque chose comme la notation BNF. Je suppose que vous avez fait cela pour la langue que vous avez créée.
Sémantique
Bien, nous savons ce qu’est un programme, mais c’est juste 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. De manière générale, il existe deux approches: la sémantique dénotationnelle et la 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.). cela donne un sens à notre programme. La sémantique opérationnelle, au contraire, définit un programme en détaillant son exécution. À mon avis, la sémantique opérationnelle est plus intuitive pour les programmeurs (y compris moi-même), alors restons avec cela.
Je ne vais pas vous expliquer comment définir une sémantique opérationnelle formelle (les détails sont un peu compliqués), mais au fond, nous voulons des règles comme celles-ci:
num[n]
est une valeur
str[s]
est une valeur
- Si
num[n1]
et num[n2]
évalue les entiers n_1$ and $n_2$, then
plus (num [n1], num [n2]) `correspond à l'entier $ n_1 + n_2 $.
- Si
str[s1]
et str[s2]
évalue les chaînes s1 et s2, alors concat(str[s1], str[s2])
évalue la chaîne s1s2.
Etc. Les règles sont en pratique beaucoup plus formelles, mais vous comprenez l'essentiel. Cependant, nous rencontrons rapidement un problème. Que se passe-t-il lorsque nous écrivons ce qui suit:
concat(num[5], str[hello])
Hm. C'est tout à fait une énigme. Nous n'avons défini nulle part la 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 une signification grâce aux 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. De nouveau, comme pour les règles d’évaluation, je présenterai les règles de dactylographie de manière informelle, mais elles peuvent être rigoureuses. Voici quelques règles:
- Un jeton de la forme
num[n]
a le type nat
.
- Un jeton de la forme
str[s]
a le type str
.
- Si expression
e1
a le type nat
et expression e2
a le type nat
, l'expression plus(e1, e2)
a le type nat
.
- Si expression
e1
a le type str
et expression e2
a le type str
, alors expression concat(e1, e2)
a le type str
.
Ainsi, selon ces règles, il y plus(num[5], num[2])
a a 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 attribuer n'importe quel type, et sinon, il est mal typé. Un système de type est sain si tous les programmes bien typés peuvent être exécutés. Haskell est sain; C n'est pas.
Conclusion
Il existe d'autres points de vue sur les types. Les types, dans un certain sens, correspondent à la logique intuitionniste et peuvent également être considérés comme des objets dans la théorie des catégories. Comprendre ces connexions est fascinant, mais ce n’est pas essentiel si l’on veut simplement écrire ou même concevoir un langage de programmation. Cependant, la compréhension des types en tant qu'outil de contrôle des formations de programme est essentielle pour la conception et le développement de langages 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.