Typage progressif: «Presque toutes les langues avec un système de type statique ont également un système de type dynamique»


20

Cette affirmation d' Aleks Bromfield déclare:

Presque toutes les langues avec un système de type statique ont également un système de type dynamique. Mis à part C, je ne peux pas penser à une exception

Est-ce une réclamation valide? Je comprends qu'avec les classes Reflection ou Loading au moment de l'exécution, Java ressemble un peu à cela - mais cette idée de «typage progressif» peut-elle être étendue à un grand nombre de langues?


Je ne suis pas un expert en langue, mais quelques-uns qui me viennent instantanément à l'esprit que je ne pense pas avoir de types dynamiques (nativement, ne pas dire que vous ne pouvez pas bricoler quelque chose ensemble) - Fortran, Ada, Pascal, Cobol
mattnz

4
Je suis un expert en langues et je ne sais pas trop ce que ce type prétend. «statique» et «dynamique» sont des malentendus et indiquent généralement l'heure de vérification / analyse de type (que ce soit au moment de la compilation ou au moment de l'exécution). Certains langages concilient les deux (par exemple en interdisant les opérations sur les types non pris en charge lors de la compilation et en levant des exceptions comme ClassCastException pendant l'exécution), et c'est peut-être ce qu'il veut dire. Si c'est le cas, il est vrai que C n'effectue généralement pas de vérification de type pendant l'exécution. Dire une langue. n'a pas de "système de type dynamique" n'a pas de sens.
Thiago Silva

Réponses:


37

Tweeter original ici. :)

Tout d'abord, je suis quelque peu amusé / choqué que mon tweet soit pris si au sérieux! Si j'avais su que ça allait être aussi largement diffusé, j'aurais passé plus de 30 secondes à l'écrire!

Thiago Silva a raison de souligner que «statique» et «dynamique» décrivent plus précisément la vérification de type , plutôt que les systèmes de type . En fait, il n'est pas vraiment exact de dire qu'une langue est typée de manière statique ou dynamique. Au contraire, une langue a un système de types, et une implémentation de cette langue peut imposer le système de types à l'aide de la vérification statique, de la vérification dynamique, ou des deux, ou des deux (bien que ce ne soit pas une implémentation de langage très attrayante!).

En fait, il existe certains systèmes de types (ou caractéristiques de systèmes de types) qui se prêtent mieux à la vérification statique, et il existe certains systèmes de types (ou caractéristiques de systèmes de types) qui se prêtent mieux à la vérification dynamique. Par exemple, si votre langue vous permet de spécifier dans le texte d'un programme qu'une valeur particulière doit toujours être un tableau d'entiers, il est alors assez simple d'écrire un vérificateur statique pour vérifier cette propriété. Inversement, si votre langue a un sous-typage, et si elle autorise le downcasting, alors il est raisonnablement simple de vérifier la validité d'un downcast au moment de l'exécution, mais il est extrêmement difficile de le faire au moment de la compilation.

Ce que je voulais vraiment dire par mon tweet, c'est simplement que la grande majorité des implémentations de langage effectuent une certaine quantité de vérification de type dynamique. Ou, de manière équivalente, la grande majorité des langues ont des fonctionnalités qui sont difficiles (voire impossibles) à vérifier statiquement. Le downcasting en est un exemple. D'autres exemples incluent le débordement arithmétique, la vérification des limites du tableau et la vérification nulle. Certains d'entre eux peuvent être vérifiés statiquement dans certaines circonstances, mais dans l'ensemble, vous auriez du mal à trouver une implémentation de langage qui ne fait aucune vérification au moment de l'exécution.

Ce n'est pas une mauvaise chose. C'est juste une observation qu'il existe de nombreuses propriétés intéressantes que nous aimerions que nos langues appliquent, et que nous ne savons pas vraiment comment vérifier statiquement. Et c'est un rappel que les distinctions comme les "types statiques" par rapport aux "types dynamiques" ne sont pas aussi nettes que certains le voudraient. :)

Une dernière remarque: les termes «fort» et «faible» ne sont pas vraiment utilisés dans la communauté de recherche en langage de programmation, et ils n'ont pas vraiment de sens cohérent. En général, j'ai trouvé que lorsque quelqu'un dit qu'une langue a un "typage fort" et qu'une autre langue a un "typage faible", ils disent vraiment que leur langue préférée (celle avec "typage fort") les empêche de faire une erreur que l'autre langue (celle avec "typage faible") ne fait pas - ou inversement, que leur langue préférée (celle avec "typage faible") leur permet de faire quelque chose de cool que l'autre langue (le celui avec "typage fort") ne le fait pas.


9
"C'est juste une observation qu'il existe de nombreuses propriétés intéressantes que nous aimerions que nos langues appliquent, et que nous ne savons pas vraiment comment vérifier statiquement." - Eh bien, ce n'est pas seulement que nous ne savons pas comment le faire. « Est -ce que cet arrêt de programme pour cette entrée » est une propriété intéressante que nous ne ne savons pas comment vérifier, mais en fait , nous ne savoir est impossible de vérifier.
Jörg W Mittag

"D'autres exemples incluent le débordement arithmétique, la vérification des limites du tableau et la vérification nulle." Juste une note que bien qu'il existe quelques langues qui vérifient ces propriétés dans le système de type, même dans la plupart des langues typées dynamiquement, ce sont généralement des caractéristiques de valeurs , pas de types. (La vérification "Null" est cependant une caractéristique courante des systèmes de type statique.)
porglezomp

Il existe un système de type dynamique et un système de type statique . Même si aucun n'est appliqué, ils sont toujours là, décrivant ce qui est correct et ce qui est une erreur. Le langage / l'implémentation peut ou non vérifier l'un ou l'autre. BTW, le système de type statique et dynamique devrait être cohérent, mais ce n'est pas le cas par définition .
Elazar

Chaque langue a un système de type dynamique, qui est un ensemble de règles interdisant l'exécution de certaines opérations.
Elazar

7

Hé bien oui. Vous pouvez avoir des sacs de propriété dans n'importe quelle langue tapée statiquement. La syntaxe sera terrible alors qu'en même temps vous gagnerez tous les inconvénients d'un système typé dynamiquement. Il n'y a donc pas vraiment d'avantage à moins que le compilateur ne vous permette d'utiliser une syntaxe plus agréable, comme le fait C # dynamic.

Vous pouvez également le faire assez facilement en C aussi.

En réaction à d'autres réponses: je pense que les gens confondent le typage statique / dynamique avec le typage fort / faible. La frappe dynamique consiste à pouvoir modifier la structure des données lors de l'exécution et le code à utiliser des données qui correspondent exactement aux besoins du code. Cela s'appelle Duck Typing .

Mentionner la réflexion ne raconte pas toute l'histoire, car la réflexion ne vous permet pas de modifier la structure existante des données. Vous ne pouvez pas ajouter de nouveau champ à une classe ou une structure en C, C ++, Java ou C #. Dans les langages dynamiques, l'ajout de nouveaux champs ou attributs aux classes existantes est non seulement possible, mais en fait assez courant.

Par exemple, regardez Cython , le compilateur Python-to-C. Il crée du code C statique, mais le système de type conserve sa nature dynamique. C est un langage typé statiquement, mais il est capable de prendre en charge le typage dynamique à partir de Python.


Techniquement, vous pouvez le faire en C # à partir de la version 4 avec ExpandoObject, bien que ce soit un processus opt-in, contrairement à JavaScript ou Ruby. Pourtant, vous avez fait une remarque très importante, qui est que la saisie de canard (ce que 99% des développeurs veulent réellement dire quand ils disent "typé dynamiquement") et la réflexion ne sont pas du tout les mêmes choses.
Aaronaught

Puis-je également ajouter que Python n'a pas de véritable typage de canard. Il a juste quelques crochets pour vous "implémenter le vôtre" (a une méthode magique qui peut retourner Truepour dire "cet objet fou est une instance de la classe que je définis"). OCaml a cette fonctionnalité pour autant que je sache, mais je ne sais pas vraiment.
vlad-ardelean

6

Les langues dynamiques sont des langues statiques . Ce qui est communément appelé "typage dynamique" est vraiment un cas particulier de typage statique - le cas où vous vous êtes limité à n'avoir qu'un seul type. Comme une expérience de réflexion, imaginez écrire un programme en Java ou C # en utilisant uniquement des Objectvariables / champs / paramètres et en effectuant une conversion immédiatement avant d'appeler une méthode. Il serait plus précis d'appeler des langages tels que Python ou Javascript "unie". (Cette affirmation va probablement dérouter / déranger beaucoup de gens, étant donné qu'un tel programme Java ou C # utiliserait de nombreux sous-types, mais c'est parce que le langage OOP moyen confond les types et les classes. Lisez le billet de blog pour plus de détails.)

Notez que même C a un typage "dynamique" - vous pouvez convertir n'importe quel pointeur en un pointeur void(et si la mémoire me sert char) et inversement. Et notez également qu'il n'y a pas de vérification d'exécution là-bas; si vous vous trompez, profitez de votre comportement indéfini!


Mais le casting se fait statiquement. Je ne vois pas comment ceci est un exemple de frappe dynamique.
osa

@osa Tout simplement parce que le code dit que String foo = (String) barcela ne signifie pas que barc'est en fait un String. Vous ne pouvez le savoir qu'au moment de l'exécution, donc je ne vois pas comment le cast est fait "statiquement".
Doval

Je ne suis pas d'accord avec ça. Au moins en Javascript, vous pouvez ajouter de nouvelles méthodes et propriétés aux objets lors de l'exécution. En C #, vous devez utiliser explicitement un dynamicobjet pour ce faire. Si vous essayez d'ajouter une propriété à un object... eh bien, vous ne pouvez pas.
Arturo Torres Sánchez

3

La différence entre le typage statique et dynamique est lorsque le type d'une valeur est vérifié: temps de compilation vs temps d'exécution. Dans les langages où les valeurs portent leur type avec eux (par exemple les objets Java), vous pouvez toujours recourir au typage dynamique même lorsque le langage préfère réellement le typage statique. Voici un exemple en Java, avec une méthode typée dynamiquement:

void horribleJava(List untypedList) {
  for (Object o : untypedList)
    ((SpecificType) o).frobnicate();
}

Remarquez comment le type de chaque élément est vérifié lors de l'exécution. La méthode équivalente typée statiquement est:

void goodJava(List<SpecificType> typedList) {
  for (SpecificType o : typedList) {
    o.forbnicate();
  }
}

En C, les valeurs (et en particulier les pointeurs) ne conservent pas leur type lors de l'exécution - chaque pointeur est équivalent à a void *. Au lieu de cela, les variables et les expressions ont un type. Pour obtenir une frappe dynamique, vous devez transporter vous-même les informations de type (par exemple, en tant que champ dans une structure).


1
Je ne pense pas qu'une distribution compte comme une frappe dynamique dans un sens vraiment pratique. Il nécessite toujours la connaissance du type au moment de la compilation, il diffère simplement la validation réelle jusqu'à l'exécution. Vous ne pouvez pas invoquer la frobnicateméthode ici sans le savoir SpecificType.
Aaronaught

2
@Aaronaught Mais il s'agit de frappe dynamique une fois que nous avons différé la vérification de type au moment de l'exécution. Notez que mes deux exemples utilisent un typage nominatif qui nécessite un nom de type spécifique. Vous pensez au typage structurel , par exemple au typage canard qui nécessite la présence d'une méthode. Les axes structurels vs nominatifs et statiques vs dynamiques sont orthogonaux les uns aux autres (Java est nominatif et principalement statique; ML et Go sont structurels et statiques; Perl, Python et Ruby sont (principalement) structurels et dynamiques).
amon

Comme je l'ai dit dans mon dernier commentaire, c'est une distinction théorique qu'aucun programmeur que j'ai jamais rencontré ne se soucie vraiment. Vous pouvez affirmer de manière abrupte que les gens utilisent la mauvaise terminologie (et il semble en fait que le tweeter d'origine visait précisément ce genre de snarkiness), mais pour les gens qui sont réellement dans les tranchées, le typage dynamique = le typage de canard. En fait, cette définition est si courante que des langues telles que C # l'ont effectivement codifiée (c'est-à-dire avec le dynamicmot - clé). Assimiler statique à la compilation et dynamique à l' exécution ne fait généralement qu'embrouiller les eaux.
Aaronaught

@Aaronaught: Si l'on effectue un cast vers une interface, et si les classes qui implémentent une méthode avec la signification voulue implémentent systématiquement la même interface pour le dire, alors on serait essentiellement du type de canard au moment de l'exécution. Alors que certains pourraient faire valoir que le "typage du canard" devrait simplement utiliser le nom de la méthode, je pense qu'il serait plus utile de considérer le "vrai" nom de la méthode comme étant le nom de l'interface plus le nom de la méthode. Dans le cas contraire, devrait [1,2].Add([3,4])céder [1,2,3,4], [1,2,[3,4]]ou [4,6]?
supercat

@supercat: rien de ce qui précède, il devrait échouer car il n'y a pas de Addméthode sur le tableau qui accepte un tableau car une telle méthode serait ambiguë. Duck typing ne vous excuse pas d'écrire des types et des fonctions compréhensibles.
Aaronaught

2

Le typage statique ou dynamique fait essentiellement référence à la façon dont les types sont vérifiés. Le typage statique signifie que la vérification des types de diverses variables ou expressions est vérifiée sur la base du code réel (généralement par le compilateur) tandis que dans un système de type dynamique, cette vérification n'est effectuée qu'au moment de l'exécution, par l'environnement d'exécution.

Ce que je crois que le texte fait référence, c'est que même si les types sont effectivement vérifiés statiquement, ils sont également vérifiés au moment de l'exécution, c'est-à-dire dynamiquement. Vous avez correctement mentionné Java Reflection; la réflexion ne se produit qu'au moment de l'exécution et Java Runtime Environment (JVM) effectue en fait la vérification de type lorsque la réflexion est utilisée, ce qui signifie essentiellement une vérification de type dynamique.


Alors ce n'est pas correct. En C ++, Pascal, Fortran --- dans tous les langages compilés --- les types ne sont vérifiés que statiquement et non dynamiquement (sauf si vous utilisez dynamic_cast). Vous pouvez utiliser des pointeurs pour écrire des éléments arbitraires dans la mémoire, et personne ne connaîtra les types. Ainsi, la frappe statique signifie UNIQUEMENT VÉRIFICATION STATIQUE, tandis que la frappe dynamique signifie UNIQUEMENT VÉRIFICATION AU RUNTIME.
osa

-1

L'exception est fausse: C possède également un système de type dynamique important. Il ne le vérifie tout simplement pas ("C est fortement typé, faiblement vérifié"). Par exemple, le traitement d'une structure comme un double( reinternpret_cast-style) produit un comportement non défini - une erreur de type dynamique.


(À tous ceux qui ont voté contre - le commentaire de @Thiago Silva dit à propos de la même chose)
Elazar
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.