Système de types

0
Apprendre encore plus Cet article a plusieurs problèmes. Aidez -nous à l’améliorer ou discutez de ces problèmes sur la page de discussion . ( Apprenez comment et quand supprimer ces modèles de messages )

Learn more This article is written like a personal reflection, personal essay, or argumentative essay that states a Wikipedia editor’s personal feelings or presents an original argument about a topic. Please help improve it by rewriting it in an encyclopedic style. (July 2016) (Learn how and when to remove this template message)

(Learn how and when to remove this template message)

Dans les langages de programmation , un système de types est un Système logique comprenant un ensemble de règles qui attribue une propriété appelée type aux différentes constructions d’un programme informatique , telles que des variables , des expressions , des fonctions ou des modules . [1] Ces types formalisent et appliquent les catégories par ailleurs implicites que le programmeur utilise pour les types de données algébriques , les structures de données ou d’autres composants (par exemple “chaîne”, “tableau de flotteurs”, “fonction renvoyant un booléen”). Le but principal d’un système de type est de réduire les possibilités de boguesdans les programmes informatiques [2] en définissant des interfaces entre différentes parties d’un programme informatique, puis en vérifiant que les parties ont été connectées de manière cohérente. Cette vérification peut se produire de manière statique (au moment de la compilation ), dynamiquement (au moment de l’exécution ) ou en combinant les deux. Les systèmes de type ont également d’autres objectifs, tels que l’expression de règles métier, l’activation de certaines optimisations du compilateur , la possibilité d’ une répartition multiple , la fourniture d’une forme de documentation, etc.

Un système de type associe un type à chaque valeur calculée et, en examinant le flux de ces valeurs, tente de garantir ou de prouver qu’aucune erreur de type ne peut se produire. Le système de type donné en question détermine ce qui constitue une erreur de type, mais en général, le but est d’empêcher que des opérations attendant un certain type de valeur soient utilisées avec des valeurs pour lesquelles cette opération n’a pas de sens (erreurs de validité). Les systèmes de types sont souvent spécifiés dans le cadre des langages de programmation et intégrés aux interpréteurs et aux compilateurs, bien que le système de types d’un langage puisse être étendu par des outils facultatifs qui effectuent des vérifications supplémentaires à l’aide de la syntaxe et de la grammaire de type d’origine du langage.

Aperçu de l’utilisation

Un exemple de système de typage simple est celui du langage C. Les parties d’un programme C sont les définitions de fonctions . Une fonction est appelée par une autre fonction. L’interface d’une fonction indique le nom de la fonction et une liste de paramètres qui sont passés au code de la fonction. Le code d’une fonction invoquante indique le nom de la fonction invoquée, ainsi que les noms des variables qui contiennent des valeurs à lui transmettre. Lors de l’exécution, les valeurs sont placées dans la mémoire temporaire, puis l’exécution saute au code de la fonction invoquée. Le code de la fonction invoquée accède aux valeurs et les utilise. Si les instructions à l’intérieur de la fonction sont écrites avec l’hypothèse de recevoir une valeur entière , mais que le code appelant a passé unvaleur à virgule flottante , alors le mauvais résultat sera calculé par la fonction invoquée. Le compilateur C vérifie les types des arguments passés à une fonction lorsqu’elle est appelée par rapport aux types des paramètres déclarés dans la définition de la fonction. Si les types ne correspondent pas, le compilateur génère une erreur de compilation.

Un compilateur peut également utiliser le type statique d’une valeur pour optimiser le stockage dont il a besoin et le choix des algorithmes pour les opérations sur la valeur. Dans de nombreux compilateurs C , le type de données float , par exemple, est représenté sur 32 bits , conformément à la spécification IEEE pour les nombres à virgule flottante simple précision . Ils vont donc utiliser des opérations du microprocesseur spécifiques à la virgule flottante sur ces valeurs (addition en virgule flottante, multiplication, etc.).

La profondeur des contraintes de type et la manière de leur évaluation affectent le typage du langage. Un langage de programmation peut en outre associer une opération à différentes résolutions pour chaque type, dans le cas du Polymorphisme de type . La théorie des types est l’étude des systèmes de types. Les types concrets de certains langages de programmation, tels que les entiers et les chaînes, dépendent de problèmes pratiques d’architecture informatique, d’implémentation de compilateur et de conception de langage.

Fondamentaux

Formellement, la théorie des types étudie les systèmes de types. Un langage de programmation doit avoir la possibilité de vérifier le type à l’aide du système de type, que ce soit au moment de la compilation ou de l’exécution, annoté manuellement ou automatiquement inféré. Comme Mark Manasse l’a dit de manière concise : [3]

Le problème fondamental adressé par une théorie des types est de s’assurer que les programmes ont un sens. Le problème fondamental causé par une théorie des types est que les programmes significatifs peuvent ne pas avoir de significations qui leur sont attribuées. La recherche de systèmes de types plus riches résulte de cette tension.

L’attribution d’un type de données, appelée typage , donne un sens à une séquence de bits telle qu’une valeur en mémoire ou à un objet tel qu’une variable . Le matériel d’un Ordinateur à usage général est incapable de faire la distinction entre, par exemple, une adresse mémoire et un code d’instruction , ou entre un caractère , un entier ou un Nombre à virgule flottante , car il ne fait aucune distinction intrinsèque entre les valeurs possibles qui une séquence de bits peut signifier . [note 1] Associer une séquence de bits à un type signifie quesignifiant au matériel programmable de former un Système symbolique composé de ce matériel et d’un programme.

Un programme associe chaque valeur à au moins un type spécifique, mais il peut également arriver qu’une valeur soit associée à plusieurs sous- types . D’autres entités, telles que des objets , des modules , des canaux de communication et des dépendances peuvent être associées à un type. Même un type peut être associé à un type. Une implémentation d’un système de types pourrait en théorie associer des identifications appelées type de données (un type d’une valeur), classe (un type d’un objet) et genre (un type d’un type ou métatype). Ce sont les abstractions que peut traverser le typage, sur une hiérarchie de niveaux contenus dans un système.

Lorsqu’un langage de programmation fait évoluer un système de type plus élaboré, il gagne un ensemble de règles plus fines que la vérification de type de base, mais cela a un prix lorsque les inférences de type (et d’autres propriétés) deviennent indécidables , et lorsqu’une plus grande attention doit être accordée par au programmeur d’annoter le code ou d’examiner les opérations et le fonctionnement liés à l’ordinateur. Il est difficile de trouver un système de typage suffisamment expressif qui satisfasse toutes les pratiques de programmation d’une manière sûre .

Plus il y a de restrictions de type imposées par le compilateur, plus un langage de programmation est fortement typé . Les langages fortement typés exigent souvent que le programmeur effectue des conversions explicites dans des contextes où une conversion implicite ne causerait aucun dommage. Le système de type de Pascal a été décrit comme “trop ​​​​fort” car, par exemple, la taille d’un tableau ou d’une chaîne fait partie de son type, ce qui rend certaines tâches de programmation difficiles. [4] [5] Haskell est également fortement typé mais ses types sont automatiquement déduits de sorte que les conversions explicites sont souvent inutiles.

Un compilateur de langage de programmation peut également implémenter un type dépendant ou un système d’effet , ce qui permet de vérifier encore plus de spécifications de programme par un vérificateur de type. Au-delà des simples paires valeur-type, une “région” virtuelle de code est associée à un composant “effect” décrivant ce qui est fait avec what , et permettant par exemple de “lancer” un rapport d’erreur. Ainsi, le Système symbolique peut être un système de type et d’effet , ce qui lui confère plus de vérification de sécurité que la vérification de type seule.

Qu’il soit automatisé par le compilateur ou spécifié par un programmeur, un système de type rend le comportement du programme illégal s’il est en dehors des règles du système de type. Les avantages fournis par les systèmes de type spécifié par le programmeur incluent :

  • Abstraction (ou modularité ) – Les types permettent aux programmeurs de penser à un niveau supérieur au bit ou à l’octet, sans se soucier de l’implémentation de bas niveau. Par exemple, les programmeurs peuvent commencer à considérer une chaîne comme un ensemble de valeurs de caractères au lieu d’un simple tableau d’octets. Plus haut encore, les types permettent aux programmeurs de penser et d’exprimer des interfaces entre deux sous-systèmes de n’importe quelle taille. Cela permet plus de niveaux de localisation afin que les définitions requises pour l’interopérabilité des sous-systèmes restent cohérentes lorsque ces deux sous-systèmes communiquent.
  • Documentation – Dans les systèmes de type plus expressifs, les types peuvent servir de forme de documentation clarifiant l’intention du programmeur. Par exemple, si un programmeur déclare une fonction comme renvoyant un type d’horodatage, cela documente la fonction lorsque le type d’horodatage peut être explicitement déclaré plus profondément dans le code comme étant un type entier.

Les avantages fournis par les systèmes de types spécifiés par le compilateur incluent :

  • Optimisation – La vérification de type statique peut fournir des informations utiles au moment de la compilation. Par exemple, si un type nécessite qu’une valeur soit alignée en mémoire sur un multiple de quatre octets, le compilateur peut être en mesure d’utiliser des instructions machine plus efficaces.
  • Sécurité – Un système de type permet au compilateur de détecter le code sans signification ou invalide. Par exemple, nous pouvons identifier une expression 3 / “Hello, World”comme non valide, lorsque les règles ne précisent pas comment diviser un entier par une chaîne . Un typage fort offre plus de sécurité, mais ne peut pas garantir une sécurité de type complète .

Erreurs de saisie

Une erreur de type est une condition non intentionnelle [ clarification nécessaire ] qui peut se manifester à plusieurs étapes du développement d’un programme. Ainsi, une installation pour la détection de l’erreur est nécessaire dans le système de type. Dans certains langages, tels que Haskell, pour lesquels l’ inférence de type est automatisée, lint peut être disponible pour son compilateur pour aider à la détection d’erreur.

La sécurité de type contribue à l ‘ exactitude du programme , mais ne peut garantir l’ exactitude qu’au prix de faire de la vérification de type elle – même un problème indécidable . [ citation nécessaire ] Dans un système de type avec vérification de type automatisée, un programme peut s’avérer fonctionner de manière incorrecte sans produire d’erreurs de compilation. La division par zéro est une opération dangereuse et incorrecte, mais un vérificateur de type exécuté uniquement au moment de la compilation ne recherche pas la division par zéro dans la plupart des langages, puis il reste une erreur d’exécution . Pour prouver l’absence de ces défauts, d’autres types de méthodes formelles , collectivement appelées analyses de programme, sont d’usage courant. Alternativement, un système de type suffisamment expressif, comme dans les langages à typage dépendant, peut empêcher ces types d’erreurs (par exemple, exprimer le type de nombres non nuls ). De plus , le test de logiciel est une méthode empirique pour trouver des erreurs que le vérificateur de type ne peut pas détecter.

Vérification de type

Le processus de vérification et d’application des contraintes des types — vérification de type — peut se produire au moment de la compilation (une vérification statique) ou au moment de l’exécution . Si une spécification de langage exige fortement ses règles de typage (c’est-à-dire, n’autorisant plus ou moins que les conversions de type automatiques qui ne perdent pas d’informations), on peut qualifier le processus de fortement typé , sinon de faiblement typé . Les termes ne sont généralement pas utilisés au sens strict.

Vérification de type statique

La vérification de type statique est le processus de vérification de la sécurité de type d’un programme basé sur l’analyse du texte d’un programme ( code source ). Si un programme passe un vérificateur de type statique, alors le programme est garanti pour satisfaire un ensemble de propriétés de sécurité de type pour toutes les entrées possibles.

La vérification de type statique peut être considérée comme une forme limitée de vérification de programme (voir la sécurité des types ) et, dans un langage de type sécurisé, peut également être considérée comme une optimisation. Si un compilateur peut prouver qu’un programme est bien typé, il n’a pas besoin d’émettre des contrôles de sécurité dynamiques, ce qui permet au binaire compilé résultant de s’exécuter plus rapidement et d’être plus petit.

La vérification de type statique pour les langages complets de Turing est intrinsèquement conservatrice. Autrement dit, si un système de typage est à la fois sain (ce qui signifie qu’il rejette tous les programmes incorrects) et décidable (ce qui signifie qu’il est possible d’écrire un algorithme qui détermine si un programme est bien typé), alors il doit être incomplet (ce qui signifie qu’il sont des programmes corrects, qui sont également rejetés, même s’ils ne rencontrent pas d’erreurs d’exécution). [6] Par exemple, considérons un programme contenant le code :

if <complex test> then <do something> else <signal that there is a type error>

Même si l’expression est <complex test>toujours évaluée trueà l’exécution, la plupart des vérificateurs de type rejetteront le programme comme étant mal typé, car il est difficile (voire impossible) pour un analyseur statique de déterminer que la elsebranche ne sera pas prise. [7] Par conséquent, un vérificateur de type statique détectera rapidement les erreurs de type dans les chemins de code rarement utilisés. Sans vérification de type statique, même les tests de couverture de code avec une couverture de 100 % peuvent être incapables de trouver de telles erreurs de type. Les tests peuvent ne pas détecter de telles erreurs de type, car la combinaison de tous les endroits où les valeurs sont créées et de tous les endroits où une certaine valeur est utilisée doit être prise en compte.

Un certain nombre de fonctionnalités de langage de programmation utiles et courantes ne peuvent pas être vérifiées de manière statique, telles que le downcasting . Ainsi, de nombreux langages auront à la fois une vérification de type statique et dynamique ; le vérificateur de type statique vérifie ce qu’il peut et les vérifications dynamiques vérifient le reste.

De nombreux langages avec vérification de type statique permettent de contourner le vérificateur de type. Certains langages permettent aux programmeurs de choisir entre la sécurité de type statique et dynamique. Par exemple, C# fait la distinction entre les variables typées statiquement et les variables typées dynamiquement . Les utilisations des premiers sont contrôlées de manière statique, tandis que les utilisations des seconds sont contrôlées de manière dynamique. D’autres langages permettent d’écrire du code dont le type n’est pas sécurisé ; par exemple, en C , les programmeurs peuvent librement convertir une valeur entre deux types quelconques ayant la même taille, subvertissant ainsi le concept de type.

Pour obtenir une liste des langages avec vérification de type statique, consultez la catégorie des langages à typage statique .

Vérification de type dynamique et informations sur le type d’exécution

La vérification de type dynamique est le processus de vérification de la sécurité de type d’un programme au moment de l’exécution. Les implémentations de langages à vérification de type dynamique associent généralement chaque objet d’exécution à une balise de type (c’est-à-dire une référence à un type) contenant ses informations de type. Ces informations de type d’exécution (RTTI) peuvent également être utilisées pour implémenter la répartition dynamique , la liaison tardive , la diffusion descendante , la réflexion et des fonctionnalités similaires.

La plupart des langages de type sécurisé incluent une certaine forme de vérification de type dynamique, même s’ils ont également un vérificateur de type statique. [ citation nécessaire ] [8] La raison en est que de nombreuses caractéristiques ou propriétés utiles sont difficiles ou impossibles à vérifier statiquement. Par exemple, supposons qu’un programme définisse deux types, A et B, où B est un sous-type de A. Si le programme essaie de convertir une valeur de type A en type B, ce qui est connu sous le nom de downcasting , alors l’opération est uniquement légale si la valeur convertie est en fait une valeur de type B. Ainsi, une vérification dynamique est nécessaire pour vérifier que l’opération est sûre. Cette exigence est l’une des critiques du downcasting.

Par définition, la vérification de type dynamique peut entraîner l’échec d’un programme à l’exécution. Dans certains langages de programmation, il est possible d’anticiper et de se remettre de ces pannes. Dans d’autres, les erreurs de vérification de type sont considérées comme fatales.

Les langages de programmation qui incluent la vérification de type dynamique mais pas la vérification de type statique sont souvent appelés “langages de programmation typés dynamiquement”. Pour une liste de ces langages, consultez la catégorie des langages de programmation typés dynamiquement .

Combiner la vérification de type statique et dynamique

Certains langages autorisent à la fois le typage statique et dynamique. Par exemple, Java et certains autres langages ostensiblement typés statiquement prennent en charge la conversion descendante des types en leurs sous- types , interrogeant un objet pour découvrir son type dynamique et d’autres opérations de type qui dépendent des informations de type d’exécution. Un autre exemple est C++ RTTI . Plus généralement, la plupart des langages de programmation incluent des mécanismes de répartition sur différents « types » de données, tels que les unions disjointes , le polymorphisme d’exécution et les types variants . Même lorsqu’ils n’interagissent pas avec les annotations de type ou la vérification de type, ces mécanismes sont matériellement similaires aux implémentations de typage dynamique. Voir langage de programmationpour plus de détails sur les interactions entre le typage statique et dynamique.

Learn more.

Les objets dans les langages orientés objet sont généralement accessibles par une référence dont le type cible statique (ou type manifeste) est égal au type d’exécution de l’objet (son type latent) ou à un supertype de celui-ci. Ceci est conforme au principe de substitution de Liskov , qui stipule que toutes les opérations effectuées sur une instance d’un type donné peuvent également être effectuées sur une instance d’un sous-type. Ce concept est également connu sous le nom de polymorphisme de subsomption ou de sous-type . Dans certains langages, les sous-types peuvent également posséder respectivement des types de retour covariants ou contravariants et des types d’arguments.

Certains langages, par exemple Clojure , Common Lisp ou Cython sont vérifiés de manière dynamique par défaut, mais permettent aux programmes d’opter pour la vérification de type statique en fournissant des annotations facultatives. L’une des raisons d’utiliser de telles astuces serait d’optimiser les performances des sections critiques d’un programme. Ceci est formalisé par un typage progressif . L’environnement de programmation DrRacket , un environnement pédagogique basé sur Lisp, et un précurseur du langage Racket est également soft-typé. [9]

Inversement, à partir de la version 4.0, le langage C# fournit un moyen d’indiquer qu’une variable ne doit pas faire l’objet d’une vérification de type statique. Une variable dont le type est dynamicne sera pas soumise à une vérification de type statique. Au lieu de cela, le programme s’appuie sur les informations de type d’exécution pour déterminer comment la variable peut être utilisée. [dix]

Dans Rust , le type fournit un typage dynamique des types. [11]std::any’static

Vérification de type statique et dynamique en pratique

Le choix entre le typage statique et dynamique nécessite certains compromis .

Le typage statique peut trouver les erreurs de type de manière fiable au moment de la compilation, ce qui augmente la fiabilité du programme livré. Cependant, les programmeurs ne sont pas d’accord sur la fréquence à laquelle les erreurs de type se produisent, ce qui entraîne d’autres désaccords sur la proportion de bogues codés qui seraient détectés en représentant de manière appropriée les types conçus dans le code. [12] [13] Défenseurs du typage statique [ qui ? ] pensent que les programmes sont plus fiables lorsqu’ils ont été bien typés, alors que les partisans du typage dynamique [ qui ? ] pointent vers du code distribué qui s’est avéré fiable et vers de petites bases de données de bogues. [ citation nécessaire ]La valeur du typage statique augmente à mesure que la force du système de type augmente. Les partisans du typage dépendant , [ qui ? ] implémentés dans des langages tels que Dependent ML et Epigram , ont suggéré que presque tous les bogues peuvent être considérés comme des erreurs de type, si les types utilisés dans un programme sont correctement déclarés par le programmeur ou correctement déduits par le compilateur. [14]

Le typage statique se traduit généralement par un code compilé qui s’exécute plus rapidement. Lorsque le compilateur connaît les types de données exacts utilisés (ce qui est nécessaire pour la vérification statique, soit par déclaration, soit par inférence), il peut produire un code machine optimisé. Certains langages typés dynamiquement tels que Common Lisp autorisent des déclarations de type facultatives pour l’optimisation pour cette raison.

En revanche, le typage dynamique peut permettre aux compilateurs de s’exécuter plus rapidement et aux interpréteurs de charger dynamiquement du nouveau code, car les modifications apportées au code source dans les langages typés dynamiquement peuvent entraîner moins de vérifications à effectuer et moins de code à revisiter. [ clarification nécessaire ] Cela aussi peut réduire le cycle édition-compilation-test-débogage.

Les langages à typage statique dépourvus d’ inférence de type (tels que C et Java avant la version 10 ) exigent que les programmeurs déclarent les types qu’une méthode ou une fonction doit utiliser. Cela peut servir de documentation de programme supplémentaire, qui est active et dynamique, au lieu de statique. Cela permet à un compilateur de l’empêcher de dériver de la synchronisation et d’être ignoré par les programmeurs. Cependant, un langage peut être typé statiquement sans nécessiter de déclarations de type (par exemple, Haskell , Scala , OCaml , F# et, dans une moindre mesure, C# et C++), la déclaration de type explicite n’est donc pas une exigence nécessaire pour le typage statique dans tous les langages.

Le typage dynamique permet des constructions que certaines vérifications de type statique (simples) rejetteraient comme illégales. Par exemple, les fonctions eval , qui exécutent des données arbitraires sous forme de code, deviennent possibles. Une fonction eval est possible avec un typage statique, mais nécessite des utilisations avancées des types de données algébriques . En outre, le typage dynamique s’adapte mieux au code de transition et au prototypage, par exemple en permettant à une structure de données d’espace réservé ( objet fictif ) d’être utilisée de manière transparente à la place d’une structure de données complète (généralement à des fins d’expérimentation et de test).

Le typage dynamique permet généralement le typage canard (ce qui permet une réutilisation plus facile du code ). De nombreux langages [ spécifier ] avec typage statique comportent également un typage canard ou d’autres mécanismes comme la programmation générique qui permettent également une réutilisation plus facile du code.

Le typage dynamique facilite généralement l’utilisation de la métaprogrammation . Par exemple, les modèles C++ sont généralement plus lourds à écrire que le code Ruby ou Python équivalent , car C++ a des règles plus strictes concernant les définitions de type (pour les fonctions et les variables). Cela oblige un développeur à écrire plus de code passe -partout pour un modèle qu’un développeur Python n’en aurait besoin. Constructions d’exécution plus avancées telles que les métaclasses et l’ introspectionsont souvent plus difficiles à utiliser dans les langages à typage statique. Dans certains langages, ces fonctionnalités peuvent également être utilisées, par exemple, pour générer de nouveaux types et comportements à la volée, sur la base de données d’exécution. Ces constructions avancées sont souvent fournies par des langages de programmation dynamiques ; beaucoup d’entre eux sont typés dynamiquement, bien que le typage dynamique n’ait pas besoin d’être lié aux langages de programmation dynamiques .

Systèmes de type fort et faible

Les langues sont souvent appelées familièrement fortement typées ou faiblement typées . En fait, il n’existe pas de définition universellement acceptée de ce que signifient ces termes. En général, il existe des termes plus précis pour représenter les différences entre les systèmes de types qui amènent les gens à les appeler “forts” ou “faibles”.

Sécurité de type et sécurité de la mémoire

Une troisième façon de catégoriser le système de types d’un langage de programmation est la sécurité des opérations typées et des conversions. Les informaticiens utilisent le terme langage de type sécurisé pour décrire les langages qui n’autorisent pas les opérations ou les conversions qui violent les règles du système de type.

Les informaticiens utilisent le terme langage sécurisé pour la mémoire (ou simplement langage sécurisé ) pour décrire les langages qui ne permettent pas aux programmes d’accéder à la mémoire qui n’a pas été affectée à leur utilisation. Par exemple, un langage sécurisé en mémoire vérifiera les limites du tableau , ou bien garantira statiquement (c’est-à-dire au moment de la compilation avant l’exécution) que les accès au tableau en dehors des limites du tableau entraîneront des erreurs de compilation et peut-être d’exécution.

Considérez le programme suivant d’un langage qui est à la fois type-safe et memory-safe : [15]

var x := 5; var y := “37”; var z := x + y;

Dans cet exemple, la variable zaura la valeur 42. Bien que ce ne soit peut-être pas ce que le programmeur avait prévu, il s’agit d’un résultat bien défini. S’il ys’agissait d’une chaîne différente, qui ne pouvait pas être convertie en nombre (par exemple “Hello World”), le résultat serait également bien défini. Notez qu’un programme peut être de type sécurisé ou sécurisé en mémoire et toujours planter sur une opération non valide. C’est pour les langages où le système de type n’est pas suffisamment avancé pour spécifier précisément la validité des opérations sur tous les opérandes possibles. Mais si un programme rencontre une opération dont le type n’est pas sécurisé, l’arrêt du programme est souvent la seule option.

Considérons maintenant un exemple similaire en C :

int x = 5 ; char y [] = “37” ; caractère * z = x + y ; printf ( “%c n ” , * z );

Dans cet exemple , pointera vers une adresse mémoire cinq caractères au-delà de , ce qui équivaut à trois caractères après le caractère zéro de fin de la chaîne pointée par . Il s’agit de la mémoire à laquelle le programme n’est pas censé accéder. En termes C, il s’agit simplement d’ un comportement indéfini et le programme peut faire n’importe quoi ; avec un simple compilateur, il pourrait en fait imprimer n’importe quel octet stocké après la chaîne “37”. Comme le montre cet exemple, C n’est pas sécurisé en mémoire. Comme les données arbitraires étaient supposées être un caractère, ce n’est pas non plus un langage de type sécurisé.zyy

En général, la sécurité du type et la sécurité de la mémoire vont de pair. Par exemple, un langage qui prend en charge l’arithmétique de pointeur et les conversions de nombre à pointeur (comme C) n’est ni sécurisé en termes de mémoire ni de type, car il permet d’accéder à une mémoire arbitraire comme s’il s’agissait d’une mémoire valide de n’importe quel type.

Pour plus d’informations, consultez Sécurité de la mémoire .

Niveaux variables de vérification de type

Certains langages autorisent différents niveaux de vérification à appliquer à différentes régions de code. Les exemples comprennent:

  • La use strictdirective en JavaScript [16] [17] [18] et Perl applique une vérification plus forte.
  • Le declare(strict_types=1)en PHP [19] sur une base par fichier permet que seule une variable de type exact de la déclaration de type soit acceptée, ou qu’une TypeErrorsoit levée.
  • Le Option Strict Onin VB.NET permet au compilateur d’exiger une conversion entre les objets.

Des outils supplémentaires tels que lint et IBM Rational Purify peuvent également être utilisés pour atteindre un niveau de rigueur plus élevé.

Systèmes de type optionnels

Il a été proposé, principalement par Gilad Bracha , que le choix du système de type soit fait indépendamment du choix de la langue ; qu’un système de typage devrait être un module qui peut être connecté à un langage selon les besoins. Il pense que cela est avantageux, car ce qu’il appelle les systèmes de types obligatoires rend les langues moins expressives et le code plus fragile. [20] L’exigence que le système de type n’affecte pas la sémantique de la langue est difficile à remplir.

Le typage facultatif est lié au typage graduel , mais distinct de celui-ci . Bien que les deux disciplines de typage puissent être utilisées pour effectuer une analyse statique du code ( typage statique ), les systèmes de typage facultatifs n’appliquent pas la sécurité des types au moment de l’exécution ( typage dynamique ). [20] [21]

Polymorphisme et types

Le terme polymorphisme fait référence à la capacité du code (en particulier, des fonctions ou des classes) à agir sur des valeurs de plusieurs types, ou à la capacité de différentes instances de la même structure de données à contenir des éléments de types différents. Les systèmes de types qui autorisent le polymorphisme le font généralement afin d’améliorer le potentiel de réutilisation du code : dans un langage avec polymorphisme, les programmeurs n’ont besoin d’implémenter une structure de données telle qu’une liste ou un tableau associatif qu’une seule fois, plutôt qu’une seule fois pour chaque type de élément avec lequel ils envisagent de l’utiliser. Pour cette raison, les informaticiens appellent parfois l’utilisation de certaines formes de polymorphisme programmation générique . Les fondements de la théorie des types du polymorphisme sont étroitement liés à ceux de l’abstraction ,modularité et (dans certains cas) sous- typage .

Systèmes de type spécialisé

De nombreux systèmes de type ont été créés qui sont spécialisés pour une utilisation dans certains environnements avec certains types de données, ou pour l’analyse de programme statique hors bande . Souvent, ceux-ci sont basés sur des idées de la théorie des types formelles et ne sont disponibles que dans le cadre de systèmes de recherche prototypes.

Le tableau suivant donne un aperçu des concepts théoriques de type utilisés dans les systèmes de typage spécialisés. Les noms M, N, O s’étendent sur les termes et les noms σ , τ {displaystyle sigma ,tau } {displaystyle sigma ,tau } {displaystyle sigma ,tau }gamme sur les types. La notation τ [ α := σ ] {displaystyle tau [alpha :=sigma ]} {displaystyle tau [alpha :=sigma ]} {displaystyle tau [alpha :=sigma ]}(resp. τ [ x := N ] {displaystyle tau [x:=N]} {displaystyle tau [x:=N]} {displaystyle tau [x:=N]}) décrit le type qui résulte du remplacement de toutes les occurrences du type variable α (resp. terme variable x ) dans τ par le type σ (resp. terme N ).

Notion de type Notation Sens
Une fonction σ → τ { displaystyle sigma à tau} sigma to tau sigma to tau Si M est de type σ → τ { displaystyle sigma à tau} sigma to tau sigma to tau et N est de type σ , alors l’application M ( N ) {displaystyle M(N)} {displaystyle M(N)} {displaystyle M(N)}est de type τ .
Produit σ × τ { displaystyle sigma fois tau} {displaystyle sigma times tau } {displaystyle sigma times tau } Si M est de type σ × τ { displaystyle sigma fois tau} {displaystyle sigma times tau } {displaystyle sigma times tau }, alors M = ( N , O ) {displaystyle M=(N,O)} {displaystyle M=(N,O)} {displaystyle M=(N,O)}est un couple tel que N soit de type σ et O de type τ .
Somme σ + τ {displaystyle sigma +tau} {displaystyle sigma +tau } {displaystyle sigma +tau } Si M est de type σ + τ {displaystyle sigma +tau} {displaystyle sigma +tau } {displaystyle sigma +tau }, alors soit M = ι 1 ( N ) {displaystyle M=iota _{1}(N)} {displaystyle M=iota _{1}(N)} {displaystyle M=iota _{1}(N)}est la première injection telle que N soit de type σ , ou

M = ι 2 ( N ) {displaystyle M=iota _{2}(N)} {displaystyle M=iota _{2}(N)} {displaystyle M=iota _{2}(N)}est la seconde injection telle que N soit de type τ .

Intersection σ ∩ τ { displaystyle sigma cap tau } {displaystyle sigma cap tau } {displaystyle sigma cap tau } Si M est de type σ ∩ τ { displaystyle sigma cap tau } {displaystyle sigma cap tau } {displaystyle sigma cap tau }, alors M est de type σ et M de type τ .
syndicat σ ∪ τ { displaystyle sigma cup tau } {displaystyle sigma cup tau } {displaystyle sigma cup tau } Si M est de type σ ∪ τ { displaystyle sigma cup tau } {displaystyle sigma cup tau } {displaystyle sigma cup tau }, alors M est de type σ ou M est de type τ .
Record ⟨ x : τ ⟩ {displaystyle langle x:tau rangle } {displaystyle langle x:tau rangle } {displaystyle langle x:tau rangle } Si M est de type ⟨ x : τ ⟩ {displaystyle langle x:tau rangle } {displaystyle langle x:tau rangle } {displaystyle langle x:tau rangle }, alors M a un membre x de type τ .
polymorphe ∀ α . τ {displaystyle forall alpha .tau } {displaystyle forall alpha .tau } {displaystyle forall alpha .tau } Si M est de type ∀ α . τ {displaystyle forall alpha .tau } {displaystyle forall alpha .tau } {displaystyle forall alpha .tau }, alors M est de type τ [ α := σ ] {displaystyle tau [alpha :=sigma ]} {displaystyle tau [alpha :=sigma ]} {displaystyle tau [alpha :=sigma ]}pour tout type σ .
Existentiel ∃ α . τ {displaystyle existe alpha .tau } {displaystyle exists alpha .tau } {displaystyle exists alpha .tau } Si M est de type ∃ α . τ {displaystyle existe alpha .tau } {displaystyle exists alpha .tau } {displaystyle exists alpha .tau }, alors M est de type τ [ α := σ ] {displaystyle tau [alpha :=sigma ]} {displaystyle tau [alpha :=sigma ]} {displaystyle tau [alpha :=sigma ]}pour un certain type σ .
Récursif μ α . τ {displaystyle mu alpha .tau } {displaystyle mu alpha .tau } {displaystyle mu alpha .tau } Si M est de type μ α . τ {displaystyle mu alpha .tau } {displaystyle mu alpha .tau } {displaystyle mu alpha .tau }, alors M est de type τ [ α := μ α . τ ] {displaystyle tau [alpha :=mu alpha .tau ]} {displaystyle tau [alpha :=mu alpha .tau ]} {displaystyle tau [alpha :=mu alpha .tau ]}.
Fonction dépendante ( x : σ ) → τ {displaystyle (x:sigma )à tau} {displaystyle (x:sigma )to tau } {displaystyle (x:sigma )to tau } Si M est de type ( x : σ ) → τ {displaystyle (x:sigma )à tau} {displaystyle (x:sigma )to tau } {displaystyle (x:sigma )to tau }et N est de type σ , alors l’application M ( N ) {displaystyle M(N)} {displaystyle M(N)} {displaystyle M(N)}a le type τ [ x := N ] {displaystyle tau [x:=N]} {displaystyle tau [x:=N]} {displaystyle tau [x:=N]}.
Produit dépendant ( x : σ ) × τ {displaystyle (x:sigma )times tau} {displaystyle (x:sigma )times tau } {displaystyle (x:sigma )times tau } Si M est de type ( x : σ ) × τ {displaystyle (x:sigma )times tau} {displaystyle (x:sigma )times tau } {displaystyle (x:sigma )times tau }, alors M = ( N , O ) {displaystyle M=(N,O)} {displaystyle M=(N,O)} {displaystyle M=(N,O)}est un couple tel que N soit de type σ et O de type τ [ x := N ] {displaystyle tau [x:=N]} {displaystyle tau [x:=N]} {displaystyle tau [x:=N]}.
Intersection dépendante [22] ( x : σ ) ∩ τ {displaystyle (x:sigma )cap tau} {displaystyle (x:sigma )cap tau } {displaystyle (x:sigma )cap tau } Si M est de type ( x : σ ) ∩ τ {displaystyle (x:sigma )cap tau} {displaystyle (x:sigma )cap tau } {displaystyle (x:sigma )cap tau }, alors M est de type σ et M de type τ [ x := M ] {displaystyle tau [x:=M]} {displaystyle tau [x:=M]} .
Carrefour familial [22] ⋂ x : σ τ {displaystyle bigcap _{x:sigma}tau} {displaystyle bigcap _{x:sigma }tau } {displaystyle bigcap _{x:sigma }tau } Si M est de type ⋂ x : σ τ {displaystyle bigcap _{x:sigma}tau} {displaystyle bigcap _{x:sigma }tau } , alors M est de type τ [ x := N ] {displaystyle tau [x:=N]} {displaystyle tau [x:=N]} pour tout terme N de type σ .
Union familiale [22] ⋃ x : σ τ {displaystyle bigcup _{x:sigma}tau} {displaystyle bigcup _{x:sigma }tau } {displaystyle bigcup _{x:sigma }tau } Si M est de type ⋃ x : σ τ {displaystyle bigcup _{x:sigma}tau} {displaystyle bigcup _{x:sigma }tau } {displaystyle bigcup _{x:sigma }tau }, alors M est de type τ [ x := N ] {displaystyle tau [x:=N]} {displaystyle tau [x:=N]} {displaystyle tau [x:=N]}pour un terme N de type σ .

Types dépendants

Les types dépendants sont basés sur l’idée d’utiliser des scalaires ou des valeurs pour décrire plus précisément le type d’une autre valeur. Par example, m a t r i X ( 3 , 3 ) {displaystyle mathrm {matrice} (3,3)} {displaystyle mathrm {matrix} (3,3)} {displaystyle mathrm {matrix} (3,3)}pourrait être le type d’un 3 × 3 {displaystyle 3fois 3} 3 times 3 3 times 3matrice. On peut alors définir des règles de typage comme la règle suivante pour la multiplication matricielle :

m a t r i x m u l t i p l y : m a t r i x ( k , m ) × m a t r i x ( m , n ) → m a t r i X ( k , n ) {displaystyle mathrm {matrice} _{mathrm {multiplier}} :mathrm {matrice} (k,m)times mathrm {matrice} (m,n)to mathrm {matrice} (k,n )} {displaystyle mathrm {matrix} _{mathrm {multiply} }:mathrm {matrix} (k,m)times mathrm {matrix} (m,n)to mathrm {matrix} (k,n)} {displaystyle mathrm {matrix} _{mathrm {multiply} }:mathrm {matrix} (k,m)times mathrm {matrix} (m,n)to mathrm {matrix} (k,n)}

k , m , n sont des valeurs entières positives arbitraires. Une variante de ML appelée Dependent ML a été créée sur la base de ce système de type, mais comme la vérification de type pour les types dépendants conventionnels est indécidable , tous les programmes qui les utilisent ne peuvent pas être vérifiés de type sans certaines limites. Le ML dépendant limite le type d’égalité qu’il peut décider à l’arithmétique de Presburger .

D’autres langages tels que Epigram rendent la valeur de toutes les expressions du langage décidable afin que la vérification de type puisse être décidable. Cependant, en général, la preuve de la décidabilité est indécidable , de sorte que de nombreux programmes nécessitent des annotations manuscrites qui peuvent être très non triviales. Comme cela entrave le processus de développement, de nombreuses implémentations de langage offrent une solution simple sous la forme d’une option pour désactiver cette condition. Ceci, cependant, se fait au prix de faire fonctionner le vérificateur de type dans une boucle infinie lorsqu’il alimente des programmes qui ne vérifient pas le type, provoquant l’échec de la compilation.

Types linéaires

Les types linéaires , basés sur la théorie de la logique linéaire , et étroitement liés aux types d’unicité , sont des types attribués à des valeurs ayant la propriété qu’elles ont une et une seule référence à tout moment. Celles-ci sont utiles pour décrire de grandes valeurs immuables telles que des fichiers, des chaînes, etc., car toute opération qui simultanément détruit un objet linéaire et crée un objet similaire (tel que ‘ str= str + “a”‘) peut être optimisée “sous le capot” dans un in- mutation de lieu. Normalement, cela n’est pas possible, car de telles mutations pourraient provoquer des effets secondaires sur des parties du programme contenant d’autres références à l’objet, violant la transparence référentielle . Ils sont également utilisés dans le système d’exploitation prototypeSingularité pour la communication interprocessus, garantissant de manière statique que les processus ne peuvent pas partager d’objets dans la mémoire partagée afin d’éviter les conditions de concurrence. Le langage Clean (un langage de type Haskell ) utilise ce système de type afin de gagner beaucoup de vitesse (par rapport à la réalisation d’une copie profonde) tout en restant sûr.

Types d’intersections

Les types d’ intersection sont des types décrivant des valeurs qui appartiennent à deux autres types donnés avec des ensembles de valeurs qui se chevauchent. Par exemple, dans la plupart des implémentations de C, le caractère signé a une plage de -128 à 127 et le caractère non signé a une plage de 0 à 255, de sorte que le type d’intersection de ces deux types aurait une plage de 0 à 127. Un tel type d’intersection pourrait être passé en toute sécurité en fonctions attendant des caractères signés ou non signés, car il est compatible avec les deux types.

Les types d’intersection sont utiles pour décrire les types de fonctions surchargées : par exemple, si ” → ” est le type de fonctions prenant un argument entier et renvoyant un entier, et ” → ” est le type de fonctions prenant un argument flottant et renvoyant un flottant, alors l’intersection de ces deux types peut être utilisée pour décrire les fonctions qui font l’un ou l’autre, en fonction du type d’entrée qui leur est donnée. Une telle fonction pourrait être passée dans une autre fonction attendant une fonction ” → ” en toute sécurité ; il n’utiliserait tout simplement pas la fonctionnalité ” → “.intintfloatfloatintintfloatfloat

Dans une hiérarchie de sous-classement, l’intersection d’un type et d’un type ancêtre (tel que son parent) est le type le plus dérivé. L’intersection des types frères est vide.

Le langage Forsythe inclut une implémentation générale des types d’intersection. Une forme restreinte est les types de raffinement .

Types de syndicats

Les types d’union sont des types décrivant des valeurs qui appartiennent à l’un des deux types. Par exemple, en C, le caractère signé a une plage de -128 à 127, et le caractère non signé a une plage de 0 à 255, donc l’union de ces deux types aurait une plage “virtuelle” globale de -128 à 255 qui peut être utilisé partiellement selon le membre du syndicat auquel on accède. Toute fonction gérant ce type d’union devrait traiter des entiers dans cette plage complète. Plus généralement, les seules opérations valides sur un type union sont les opérations valides sur les deux types réunis. Le concept “union” de C est similaire aux types d’union, mais n’est pas typesafe, car il permet des opérations valides sur l’un ou l’autre type, plutôt que sur les deux. Les types d’union sont importants dans l’analyse de programme, où ils sont utilisés pour représenter des valeurs symboliques dont la nature exacte (par exemple, valeur ou type) n’est pas connue.

Dans une hiérarchie de sous-classement, l’union d’un type et d’un type ancêtre (tel que son parent) est le type ancêtre. L’union des types frères est un sous-type de leur ancêtre commun (c’est-à-dire que toutes les opérations autorisées sur leur ancêtre commun sont autorisées sur le type union, mais ils peuvent également avoir d’autres opérations valides en commun).

Types existentiels

Les types existentiels sont fréquemment utilisés en relation avec les types d’enregistrement pour représenter les modules et les types de données abstraits , en raison de leur capacité à séparer l’implémentation de l’interface. Par exemple, le type “T = ∃X { a: X; f: (X → int); }” décrit une interface de module qui a un membre de données nommé a de type X et une fonction nommée f qui prend un paramètre du même type X et renvoie un entier. Cela pourrait être mis en œuvre de différentes manières; par exemple:

  • intT = { a : int ; f: (entier → entier); }
  • floatT = { a : float ; f : (float → int) ; }

Ces types sont tous deux des sous-types du type existentiel plus général T et correspondent à des types d’implémentation concrète, donc toute valeur de l’un de ces types est une valeur de type T. Étant donné une valeur “t” de type “T”, nous savons que ” tf(ta)” est bien typé, quel que soit le type abstrait X. Cela donne la flexibilité de choisir des types adaptés à une implémentation particulière tandis que les clients qui n’utilisent que des valeurs du type d’interface (le type existentiel) sont isolés de ces choix.

En général, il est impossible pour le vérificateur de type de déduire à quel type existentiel appartient un module donné. Dans l’exemple ci-dessus intT { a: int; f: (entier → entier); } pourrait aussi avoir le type ∃X { a: X; f: (entier → entier); }. La solution la plus simple consiste à annoter chaque module avec son type prévu, par exemple :

  • intT = { a : int ; f: (entier → entier); } comme ∃X { une : X ; f : (X → int) ; }

Bien que les types de données abstraits et les modules aient été implémentés dans les langages de programmation depuis un certain temps, ce n’est qu’en 1988 que John C. Mitchell et Gordon Plotkin ont établi la théorie formelle sous le slogan : “Les types [de données] abstraits ont un type existentiel”. [23] La théorie est un calcul lambda typé du second ordre similaire au système F , mais avec une quantification existentielle au lieu d’une quantification universelle.

Dactylographie progressive

Le typage progressif est un système de typage dans lequel les variables peuvent se voir attribuer un type soit au moment de la compilation (qui est un typage statique) soit au moment de l’exécution (qui est un typage dynamique), permettant aux développeurs de logiciels de choisir l’un ou l’autre paradigme de type selon le cas, de l’intérieur une seule langue. [24] En particulier, le typage graduel utilise un type spécial nommé dynamique pour représenter des types statiquement inconnus, et le typage graduel remplace la notion d’égalité de type par une nouvelle relation appelée cohérence qui relie le type dynamique à tous les autres types. La relation de cohérence est symétrique mais non transitive. [25]

Déclaration et inférence explicites ou implicites

De nombreux systèmes de types statiques, tels que ceux de C et Java, nécessitent des déclarations de type : le programmeur doit associer explicitement chaque variable à un type spécifique. D’autres, comme Haskell, effectuent une inférence de type : le compilateur tire des conclusions sur les types de variables en fonction de la façon dont les programmeurs utilisent ces variables. Par exemple, étant donné une fonction qui additionne et ensemble, le compilateur peut en déduire que et doivent être des nombres, puisque l’addition n’est définie que pour les nombres. Ainsi, tout appel à ailleurs dans le programme qui spécifie un type non numérique (tel qu’une chaîne ou une liste) comme argument signalerait une erreur.f(x, y)xyxyf

Les constantes numériques et de chaîne et les expressions dans le code peuvent impliquer et impliquent souvent un type dans un contexte particulier. Par exemple, une expression 3.14peut impliquer un type de virgule flottante , alors qu’elle peut impliquer une liste d’entiers, généralement un tableau .[1, 2, 3]

L’inférence de type est en général possible, si elle est calculable dans le système de type en question. De plus, même si l’inférence n’est pas calculable en général pour un système de type donné, l’inférence est souvent possible pour un grand sous-ensemble de programmes du monde réel. Le système de types de Haskell, une version de Hindley–Milner , est une restriction du système Fω aux types polymorphes dits de rang 1, dans lesquels l’inférence de type est calculable. La plupart des compilateurs Haskell autorisent le polymorphisme de rang arbitraire comme extension, mais cela rend l’inférence de type non calculable. (Cependant, la vérification de type est décidable et les programmes de rang 1 ont toujours une inférence de type ; les programmes polymorphes de rang supérieur sont rejetés à moins qu’ils ne reçoivent des annotations de type explicites.)

Problèmes de décision

Un système de typage qui assigne des types à des termes dans des environnements de typage à l’aide de règles de typage est naturellement associé aux problèmes de décision de vérification de type , de typabilité et d’habitation de type . [26]

  • Étant donné un environnement de type Γ {displaystylegamma} Gamma Gamma , un terme e {displaystyle e} e e, et un genre τ {displaystyletau} tau tau , décider si le terme e {displaystyle e} e epeut se voir attribuer le type τ {displaystyletau} tau tau dans l’environnement type.
  • Étant donné un terme e {displaystyle e} e e, décider s’il existe un environnement de type Γ {displaystylegamma} Gamma Gamma et un genre τ {displaystyletau} tau tau telle que le terme e {displaystyle e} e epeut se voir attribuer le type τ {displaystyletau} tau tau dans l’environnement de type Γ {displaystylegamma} Gamma Gamma .
  • Étant donné un environnement de type Γ {displaystylegamma} Gamma Gamma et un genre τ {displaystyletau} tau tau , décider s’il existe un terme e {displaystyle e} e eauquel on peut attribuer le type τ {displaystyletau} tau tau dans l’environnement type.

Système de type unifié

Certains langages comme C# ou Scala ont un système de type unifié. [27] Cela signifie que tous les types C# , y compris les types primitifs, héritent d’un seul objet racine. Chaque type en C# hérite de la classe Object. Certains langages, comme Java et Raku , ont un type racine mais aussi des types primitifs qui ne sont pas des objets. [28] Java fournit des types d’objets wrapper qui existent avec les types primitifs afin que les développeurs puissent utiliser soit les types d’objet wrapper, soit les types primitifs non objets plus simples. Raku convertit automatiquement les types primitifs en objets lors de l’accès à leurs méthodes. [29]

Compatibilité : équivalence et sous-typage

Un vérificateur de type pour un langage à typage statique doit vérifier que le type de toute expression est cohérent avec le type attendu par le contexte dans lequel cette expression apparaît. Par exemple, dans une instruction d’affectation de la forme , le type inféré de l’expression doit être cohérent avec le type déclaré ou inféré de la variable . Cette notion de cohérence, appelée compatibilité , est propre à chaque langage de programmation.x := eex

Si le type de eet le type de xsont identiques et que l’affectation est autorisée pour ce type, il s’agit d’une expression valide. Ainsi, dans les systèmes de types les plus simples, la question de savoir si deux types sont compatibles se ramène à celle de savoir s’ils sont égaux (ou équivalents ). Cependant, différents langages ont des critères différents pour déterminer quand deux expressions de type sont comprises comme dénotant le même type. Ces différentes théories équationnelles des types varient considérablement, deux cas extrêmes étant les systèmes de types structurels , dans lesquels deux types décrivant des valeurs avec la même structure sont équivalents, et les systèmes de types nominatifs , dans lesquels deux expressions de type syntaxiquement distinctes ne désignent pas le même type (c’est-à- dire que les types doivent avoir le même “nom” pour être égaux).

Dans les langues avec sous- typage , la relation de compatibilité est plus complexe. En particulier, si Best un sous-type de A, alors une valeur de type Bpeut être utilisée dans un contexte où une valeur de type Aest attendue ( covariant ), même si l’inverse n’est pas vrai. Comme l’équivalence, la relation de sous-type est définie différemment pour chaque langage de programmation, avec de nombreuses variantes possibles. La présence d’un polymorphisme paramétrique ou ad hoc dans un langage peut également avoir des implications sur la compatibilité des types.

Voir également

  • icon iconPortail de programmation informatique
  • Comparaison des systèmes de types
  • Covariance et contravariance (informatique)
  • Polymorphisme dans la programmation orientée objet
  • Règles de saisie
  • Taper la signature
  • Théorie des types

Remarques

  1. ^ La ligne informatique Burroughs ALGOL a déterminé le contenu d’un emplacement mémoire par ses bits de drapeau. Les bits d’indicateur spécifient le contenu d’un emplacement mémoire. L’instruction, le type de données et les fonctions sont spécifiés par un code à 3 bits en plus de son contenu à 48 bits. Seul le MCP (Master Control Program) pouvait écrire dans les bits de code de drapeau.

Références

  1. ^ Pierce 2002 , p. 1 : “Un système de types est une méthode syntaxique traitable pour prouver l’absence de certains comportements de programme en classant les phrases selon les types de valeurs qu’elles calculent.”
  2. ^ Cardelli 2004 , p. 1 : “Le but fondamental d’un système de type est d’empêcher l’apparition d’erreurs d’exécution lors de l’exécution d’un programme.”
  3. ^ Pierce 2002 , p. 208.
  4. ^ Tyson, JR (25 avril 1983). “JRT dit qu’il est coupable – d’avoir créé un Pascal utilisable” . Infomonde . Vol. 5, non. 1. p. 66.
  5. ^ Kernighan, Brian (1981). “Pourquoi Pascal n’est pas mon langage de programmation préféré” . Archivé de l’original le 2012-04-06 . Récupéré le 22/10/2011 .
  6. ^ “… tout système de type solide et décidable doit être incomplet” -D. Rémy (2017). p. 29, Rémy, Didier. “Systèmes de types pour les langages de programmation” (PDF) . Récupéré le 26 mai 2013 .
  7. ^ Pierce 2002 .
  8. ^ Gaurav Miglani, Gaurav (2018). “Envoi de méthodes dynamiques ou polymorphisme d’exécution en Java” . Archivé de l’original le 2020-12-07 . Récupéré le 28/03/2021 .
  9. ^ Wright, Andrew K. (1995). Pratique Soft Typing (PhD). Université du Riz. manche : 1911/16900 .
  10. ^ “dynamique (référence C#)” . Bibliothèque MSDN . Microsoft . Récupéré le 14 janvier 2014 .
  11. ^ “std :: any – Rouille” . doc.rust-lang.org . Récupéré le 07/07/2021 .
  12. ^ Meijer, Erik; Drayton, Peter. “Le typage statique si possible, le typage dynamique si nécessaire : la fin de la guerre froide entre les langages de programmation” (PDF) . Microsoft Corporation.
  13. ^ Laucher, Amanda; Snively, Paul (2012). “Types contre tests” . InfoQ.
  14. ^ Xi, Hongwei (1998). Types dépendants en programmation pratique (PhD). Département des sciences mathématiques, Université Carnegie Mellon. CiteSeerX 10.1.1.41.548 .
    Xi, Hongwei ; Pfenning, Frank (1999). “Types dépendants dans la programmation pratique”. Actes du 26e Symposium ACM SIGPLAN-SIGACT sur les principes des langages de programmation . ACM. p. 214–227. CiteSeerX 10.1.1.69.2042 . doi : 10.1145/292540.292560 . ISBN 1581130953. S2CID 245490 .
  15. ^ Visual Basic est un exemple de langage qui est à la fois type-safe et memory-safe.
  16. ^ “4.2.2 La variante stricte d’ECMAScript” . Spécification du langage ECMAScript® 2020 (11e éd.). ECMA. Juin 2020. ECMA-262.
  17. ^ “Mode strict – JavaScript” . MDN . Développeur.mozilla.org. 2013-07-03 . Récupéré le 17/07/2013 .
  18. ^ “Mode strict (JavaScript)” . MSDN . Microsoft . Récupéré le 17/07/2013 .
  19. ^ “Dactylographie stricte” . Manuel PHP : Référence du langage : Fonctions .
  20. ^ un b Bracha, G. “Types Pluggables” (PDF) .
  21. ^ “Bien sûr. C’est ce qu’on appelle la “dactylographie progressive”, et je la qualifierais de tendance. …” Existe-t-il un langage qui permette à la fois la saisie statique et dynamique ? . débordement de pile. 2012.
  22. ^ un bc Kopylov , Alexei (2003). “Intersection dépendante: une nouvelle façon de définir les enregistrements dans la théorie des types”. 18e Symposium IEEE sur la logique en informatique . LICS 2003. Société informatique IEEE. p. 86–95. CiteSeerX 10.1.1.89.4223 . doi : 10.1109/LICS.2003.1210048 .
  23. ^ Mitchell, John C.; Plotkin, Gordon D. (juillet 1988). “Les types abstraits ont un type existentiel” (PDF) . ACM Trans. Programme. Lang. Syst . 10 (3): 470–502. doi : 10.1145/44501.45065 . S2CID 1222153 .
  24. ^ Siek, Jeremy (24 mars 2014). “Qu’est-ce que la dactylographie progressive ?” .
  25. ^ Siek, Jeremy; Taha, Walid (septembre 2006). Typage progressif pour les langages fonctionnels (PDF) . Schéma et Programmation Fonctionnelle 2006 . Université de Chicago . p. 81–92.
  26. ^ Barendregt, Henk; Dekkers, Wil; Statman, Richard (20 juin 2013). Calcul lambda avec types . La presse de l’Universite de Cambridge. p. 66. ISBN 978-0-521-76614-2.
  27. ^ “8.2.4 Unification du système de types” . Spécification du langage C # (5e éd.). ECMA. Décembre 2017. ECMA-334.
  28. ^ “Types natifs” . Documentation Perl 6 .
  29. ^ “Numériques, § Auto-boxing” . Documentation Perl 6 .

Lectures complémentaires

  • Cardelli, Luca ; Wegner, Peter (décembre 1985). “Sur les types de compréhension, l’abstraction de données et le polymorphisme” (PDF) . Enquêtes informatiques ACM . 17 (4): 471–523. CiteSeerX 10.1.1.117.695 . doi : 10.1145/6041.6042 . S2CID 2921816 .
  • En lignePierce, Benjamin C. (2002). Types et langages de programmation . Presse du MIT. ISBN 978-0-262-16209-8.
  • Cardelli, Lucas (2004). “Systèmes de types” (PDF) . Dans Allen B. Tucker (éd.). CRC Handbook of Computer Science and Engineering (2e éd.). Presse CRC. ISBN 978-1584883609.
  • Tratt, Laurence (juillet 2009). “5. Langages typés dynamiquement” . Les progrès de l’informatique . Vol. 77. Elsevier. pp. 149–184. doi : 10.1016/S0065-2458(09)01205-4 . ISBN 978-0-12-374812-6.

Liens externes

Le Wikibook Ada Programming a une page sur le thème : Types
Le Wikibook Haskell a une page sur le sujet : Déclarations de classe
  • Médias liés aux systèmes de types sur Wikimedia Commons
  • Smith, Chris (2011). “Ce qu’il faut savoir avant de débattre des systèmes de type” .
You might also like
Leave A Reply

Your email address will not be published.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More