Pourquoi les variables ont-elles besoin d'un type?


10

Nous écrivons donc:

Customer c = new Customer();

Pourquoi la conception n'est-elle pas telle que nous écrivons:

c = new Customer();
c.CreditLimit = 1000;

Le compilateur peut élaborer des points c vers un client et permettre aux membres du client d'être invoqués sur c?

Je sais que nous pouvons vouloir écrire:

IPerson c = new Customer();
IPerson e = new Employee();

afin de pouvoir écrire:

public string GetName(IPerson object)
{
    return object.Name
}

string name = GetName(c); // or GetName(e);

Mais si nous écrivions:

c = new Customer();
e = new Employee();

on pourrait encore écrire:

public string GetName(object)
{
    return object.Name
}    

string name = GetName(c); // or GetName(e);

Le compilateur peut se plaindre du code immédiatement ci-dessus si le type de référence d'objet c ne prend pas en charge une propriété Name (car il peut vérifier quels membres sont utilisés sur l'argument / paramètre dans la méthode), ou le runtime peut se plaindre.

Même avec le mot clé dynamique de C #, nous utilisons toujours une variable 'type' (déterminée lors de l'exécution). Mais pourquoi une variable a-t-elle besoin d'un type? Je suis sûr qu'il doit y avoir une bonne raison, mais je n'y pense pas!


12
C'est pourquoi il existe des langages dynamiques comme Python et Ruby (et d'autres). Il n'y a pas de réponse à cette question. C'est un fait que certaines langues utilisent des déclarations de type et d'autres non.
S.Lott

2
"les variables dans ces langues n'ont pas de type du tout?" Correct. les variables n'ont pas de type en Python. Les objets ont un type et les variables sont simplement des références à des objets. Votre question n'est qu'une observation. Plus précisément, "toutes les langues ne nécessitent pas de déclarations de variables". Il n'y a pas de réponse. Il est donc probable qu'il sera fermé car non constructif.
S.Lott

1
Vous voudrez peut-être jeter un œil à Boo , qui vous permet de déclarer des variables sans aucune déclaration de type, mais utilise l' inférence de type pour comprendre les types afin de ne pas sacrifier l'exactitude et les avantages de performances d'un typage fort.
Mason Wheeler

2
var x = 1; - vouliez-vous dire 32 bits, 64 bits, 16 bits? octet? flotte? double? décimal? Il s'agit simplement de primitives.
Job

4
Vous pouvez utiliser varen C # et être exlicite sur l'endroit où vous souhaitez déclarer une variable est toujours bon.
Daniel Little

Réponses:


22

Mais pourquoi une variable a-t-elle besoin d'un type?

  1. Cela peut intercepter des bogues lorsqu'une expression invalide et mal typée est affectée à une variable. Certaines langues ont un typage dynamique , ce qui sacrifie les garanties d'exactitude d'un type par variable pour le type de flexibilité que vous semblez souhaiter.
  2. Les types peuvent permettre au compilateur de générer du code plus efficace. La frappe dynamique signifie que les vérifications de type doivent être effectuées au moment de l'exécution.

1
Veuillez expliquer le downvote.
Fred Foo

4
En outre, la frappe dynamique a un coût de performance, car les informations de type doivent être vérifiées pendant l'exécution.
Charles Salvia

@CharlesSalvia: bon point, a ajouté cela à la réponse.
Fred Foo

2
@sturdytree: quant à "s'il n'a pas de type, alors il ne peut pas être mal tapé" - en ce qui concerne les règles de langage, c'est vrai. Mais la variable peut toujours être affectée du mauvais type d'un point de vue sémantique , par exemple vous avez mal tapé un nom de constructeur et le programme fonctionne toujours, mais ne fait pas ce que vous voulez.
Fred Foo

5
@sturdytree S'il est vrai qu'il n'y a pas de langues qui ont des variables vraiment sans type avec vérification de type, il y a des langues qui ont une inférence de type . Autrement dit, le langage examine votre utilisation de la variable et déduit le type de votre utilisation. En cas de conflit (par exemple, vous effectuez a = new Bar()et appelez ensuite une méthode à partir de la classe Baz), le compilateur génère une erreur. Des langages comme Haskell et OCaml ont été les pionniers de l'inférence de type, mais il est présent en C #, avec le varmot - clé.
quanticle

9

Vous avez un point parfaitement valide, des langages qui ne gardent pas trace du type d'une variable existent et sont appelés "typés dynamiquement". La catégorie comprend des langages tels que JavaScript, Perl, Lisp et Python.

L'avantage que nous tirons d'un langage de type statique est une vérification supplémentaire des erreurs au moment de la compilation.

Supposons, par exemple, que vous disposez de la méthode suivante:

public addCustomerContact(Customer client, Employee contact) {
   ...
} 

Il serait possible, si vous avez un client bobet un employé jamesdans votre code, d'appeler par erreur addCustomerContact(james, bob), ce qui n'est pas valide. Mais si le compilateur ne connaît pas les types de variables, il ne peut pas vous avertir que vous avez effectué un appel non valide, à la place, une erreur se produit lors de l'exécution ... et puisque les langages à typage dynamique ne vérifient pas le type de paramètres transmis aux méthodes, ce problème se produit chaque fois que votre code essaie d'utiliser les propriétés client uniquement de l' jamesobjet ou les propriétés employé uniquement de l' bobobjet. Cela peut être longtemps après que la paire (james, bob) ait été ajoutée à la liste des contacts clients.

Maintenant, vous vous demandez peut-être pourquoi le compilateur ne peut pas encore déduire le type de jameset bobet nous avertir? Cela peut parfois être possible, mais si les variables n'ont vraiment aucun type, alors nous pourrions faire ce qui suit:

var james;
var bob;
if (getRandomNumber() > 0.5) {
   james = new Customer();
   bob = new Employee();
} else {
   james = new Employee();
   bob = new Customer();
}

Il est parfaitement légal d'attribuer une valeur à une variable, car nous avons dit que les variables n'ont pas de type. Cela signifie également que nous ne pouvons pas toujours connaître le type d'une variable, car elle peut être de différents types en fonction de différents chemins d'exécution.

En général, les langages typés dynamiquement sont utilisés pour les langages de script, où il n'y a pas d'étape de compilation, et donc les erreurs de compilation n'existent pas, ce qui signifie que les frappes supplémentaires nécessaires pour donner le type de variables ne seraient pas très utiles.

Il existe également des avantages distincts pour les langages typés dynamiquement, principalement en termes de moins de code nécessaire pour implémenter la même conception: les interfaces n'ont pas besoin d'être écrites, car tout est "typé canard" (nous nous soucions uniquement des méthodes / propriétés d'un objet). , pas à quelle classe appartient l'objet), il n'est pas nécessaire de donner aux variables un type explicite ... avec le compromis que nous découvrons un peu moins de bugs avant de commencer à exécuter notre code.


Merci Théodore. "Mais si le compilateur ne connaît pas les types de variables, il ne peut pas vous avertir que vous avez effectué un appel invalide" Comme vous le mentionnez, le compilateur peut connaître les types des objets vers lesquels les variables pointent.
sturdytree

Theodore: Dans votre exemple james / bob, nous devrions, en tant que programmeurs, savoir comment nous avons utilisé nos variables (et une bonne aide à la dénomination) et donc je ne vois aucun problème avec cela. Lorsque vous dites «nous ne pouvons pas toujours connaître le type d'une variable», je suppose que vous voulez dire que nous ne pouvons pas toujours connaître le type de l'objet vers lequel la variable pointe, mais le compilateur peut résoudre ce problème et ainsi avertir que des membres incorrects sont appliqué (c'est-à-dire que nous pouvons avoir une vérification statique).
sturdytree

2
J'ai donné un exemple ci-dessus dans lequel vous ne pouvez pas connaître statiquement les types d'objets ... selon le chemin d'exécution, le type d'objet stocké dans une variable sans information de type peut être différent. Vous pouvez avoir un langage comme C # où les informations de type à la compilation peuvent être déduites, mais, pour autant que je sache, il n'y a pas de langage avec à la fois une vérification de type statique et des variables vraiment sans type, la complexité de calcul de l'analyse statique est probablement aussi génial.
Theodore Murdock

Theodore, merci, vous avez bien compris que ce dont je parle est un langage avec une vérification de type statique (basée sur le type d'objet) et des variables sans type. Triste d'entendre qu'il n'y en a pas - on me dit que Python a des variables sans type, mais semble qu'il n'a pas de vérification de type statique.
sturdytree

1
-1; le typage fort / faible est orthogonal au typage statique / dynamique. C est typiquement faiblement typé; Lisp et Python sont tous deux fortement typés dynamiquement.
Fred Foo

5

Les programmeurs professionnels n'ont donc pas à déterminer si

10 + "10"

is "1010" or 20....

Qu'est-ce qu'une erreur, au moment de la compilation avec une langue typée statiquement ou à l'exécution avec une langue typée dynamiquement. Bien sûr, de toute façon.


2
C'est à dire, Perl n'est pas une langue saine? :)
Fred Foo

5
@larsmans: en fait, non, ce n'est pas le cas. mais c'est purement une opinion.
NotMe

2
@ChrisLively: Je suis content que ce soit une question de fait maintenant. Veuillez venir à tout moment sur mon lieu de travail pour convaincre mes collègues épris de Perl;)
Fred Foo

2
10 + "10" utilise un opérateur '+' sur un objet de type entier et un objet de type chaîne. Le compilateur émettrait une erreur. Ma question concerne le type de variable et non l'objet.
sturdytree

2
Il est valide C: C'est l'arithmétique des pointeurs.
dan04

4

En supposant que vous disposiez d'une variable one(définie sur 1) et que vous avez tenté d'évaluer one + one. Si vous n'aviez aucune idée du type, alors 1 + 1 sera ambigu. Vous pouvez affirmer que 2 ou 11 pourraient être des réponses correctes. Cela devient ambigu si le contexte n'est pas donné.

J'ai vu cela se produire dans SQLite où les types de bases de données ont été définis par inadvertance au VARCHARlieu de INTet lorsque les opérations ont été effectuées, les gens obtenaient des résultats inattendus.

En c # si le contexte infère un type, vous pouvez utiliser le varmot - clé.

var c = new Customer();
var e = new Employer();

Compilera c et e avec les types inférés au moment de la compilation.


1
En Python, a 1 + 1toujours le type int, mais il n'est pas nécessaire de le déclarer. La question est de savoir pourquoi les variables ont un type, pas des valeurs .
Fred Foo

Désolé, vous étiez censé voir variablespas valuesquand je l'ai utilisé 1 + 1dans mon exemple. Je suppose que ce n'était pas clair.
Stephen Quan

Pourtant, les langages typés dynamiquement peuvent gérer cela. En Python, one=1; print(one+one)imprime 2. one="1"; print(one+one)impressions 11. L'exemple SQLite est plus convaincant, mais le problème est qu'il y a un typage faible, donc ce n'est pas vraiment pertinent pour C #.
Fred Foo

Dans mon schéma suggéré, un = 1 signifie une variable pointant vers un type entier (je ne suggère pas du tout de types - les objets auraient un type). un + un ne serait alors pas ambigu (nous ajoutons deux objets / valeurs entiers)
sturdytree

1
Salut @ dan04, je l'ai vu avec ORDER BYfait par inadvertance sur un VARCHARterrain. Voir stackoverflow.com/questions/9103313/… .
Stephen Quan

4

Une variable n'a pas besoin d'avoir un type associé. Les langues où cela est vrai incluent Lisp, Scheme, Erlang, Prolog, Smalltalk, Perl, Python, Ruby et autres.

Il est également possible qu'une variable ait un type, mais vous n'aurez peut-être pas à écrire le type dans le programme. Ceci est généralement appelé inférence de type. ML, Haskell et leurs descendants ont une inférence de type puissante; certains autres langages l'ont sous des formes moindres, comme les autodéclarations C ++ .

L'argument principal contre l'inférence de type est qu'elle nuit à la lisibilité. Il est généralement plus facile de comprendre le code lorsque les types sont écrits.


1

Lorsque vous identifiez le type que représente votre variable, vous faites une déclaration sur deux ou trois choses. Vous identifiez les exigences d'allocation de mémoire pour votre variable et vous définissez des règles de compatibilité et de plage pour votre variable. Il fournit un moyen d'éviter toute confusion quant à vos intentions pour les données que vous stockez et de vous fournir un moyen relativement bon marché d'identifier les problèmes potentiels dans votre code au moment de la compilation.

Si vous déclarez les variables suivantes:

myVar      = 5;
myOtherVar = "C";

Que pouvez-vous déduire de ces variables? Est myVarsigné ou non signé? Est-ce 8 bits, 64 bits ou quelque chose entre les deux? Est-ce myOtherVarune chaîne (en fait un tableau) ou un caractère? Est-ce ANSI ou Unicode?

En fournissant des types de données spécifiques, vous fournissez au compilateur des indices sur la façon dont il peut optimiser les besoins en mémoire de votre application. Certaines langues ne se soucient pas trop de ce genre de choses, ce qui permet de traiter ces questions lors de l'exécution, tandis que d'autres langues permettent une certaine quantité de typage dynamique car en analysant le code, les types de données peuvent être déduits.

Un autre point avec les langages fortement typés, c'est qu'il vous évite d'avoir à fournir des instructions au compilateur chaque fois que vous utilisez une variable. Pouvez-vous imaginer à quel point votre code deviendrait horrible et illisible si à chaque fois que vous accédiez à une variable, vous étiez forcé de le convertir efficacement pour dire au compilateur de quel type de valeur il s'agissait? !!


Bon point, bien que le compilateur puisse simplement utiliser le type le plus efficace (par exemple, petit int) en fonction de la valeur, je peux voir que nous pouvons vouloir que "C" soit un objet chaîne plutôt que char afin de pouvoir effectuer certaines opérations . Dans de tels cas, cependant, nous pourrions simplement spécifier un = (chaîne) "C". Cela crée un objet chaîne et «a» (variable non typée) pointe simplement dessus. Je ne vois pas cela comme plus horrible que la chaîne a = "C";
sturdytree

0

Un programme informatique est un graphique de nœuds de processus décrivant ce qu'une «machine», représentée par le langage d'exécution (étendu avec des boîtes à outils dans la plupart des cas), devrait faire, dans quel ordre ou dans quelles conditions. Ce graphique est représenté par un fichier texte (ou un tas de fichiers texte) écrit dans une langue spécifique, et (partiellement ou entièrement) créé lorsque le compilateur / interprète lit (désérialise) ce fichier. En outre, il existe certains environnements (UML ou outils de génération de programmes graphiques) dans lesquels vous pouvez réellement créer ce graphique et générer le code source dans un langage cible.

Pourquoi dis-je cela? Parce que cela mène à la réponse à votre question.

Le texte de votre programme vous indique comment l'ordinateur doit résoudre la tâche réelle, contenant à la fois les étapes du processus (conditions, actions) ET la structure (quels composants utilisez-vous dans la solution). Ce dernier signifie que vous obtenez ou créez des instances d'autres composants, les placez dans des zones nommées (variables) et les utilisez: accédez à leurs données et services.

Certaines langues vous offrent des boîtes uniformes, où seule l'étiquette est importante, mais vous pouvez y mettre n'importe quoi, vous pouvez même utiliser une variable nommée "cible" pour stocker une "personne" au début et "voiture" à la fin de le même algorithme. D'autres vous obligent à créer des boîtes "en forme", donc différentes pour une personne ou une voiture - bien qu'elles vous permettent toujours de créer une "boîte générique" (objet Java, C / C ++ void *, objectif C "id" ...) et lancez-le comme vous le souhaitez. Les langages typés vous permettent d'exprimer votre structure dans des détails plus fins, créant des «contrats de type» pour vos variables (bien que vous puissiez contourner cette limitation), tandis que les langages non typés prennent en charge cette approche «je saurai sûrement ce que j'ai mis dans cette boîte cette fois» comme comportement par défaut et unique.

Les deux approches sont viables, ont leur intelligence de compilateur, de nombreux livres de programmation, des pratiques et des cadres écrits en les utilisant (et d'autres tonnes de livres sur ces cadres) à différents niveaux. Donc, aujourd'hui, la réponse semble être plus une question de goût et de connaissance de la véritable équipe de programmeurs qu'une déclaration correctement fondée, mesurée et vérifiée sur l'utilisation ou non des types.

Je pense qu'il est inutile de dire que je préfère les règles aux astuces, en particulier pour les projets à long terme, de grande équipe (aka: "sérieux"). Raison: pour autant que je sache, les causes les plus probables d'un échec / glissement d'un projet SW sont: des exigences peu claires et une mauvaise conception (80%! Selon une étude que je connais), et il ne reste que quelques pour cent pour le codage réel. Toutes les règles et tous les contrats imposent une conception plus propre, une réflexion prospective et exigent que les décisions soient prises plus tôt et par les bonnes personnes. Les types signifient des règles: moins de «liberté» et de «fraîcheur» - plus de préparation, de réflexion, de normes de codage, un travail d'équipe contrôlé. Pour moi, cela sonne comme un facteur de réussite indispensable, et aussi "chez soi, chez soi".

Mes 2 cents.


-3

AFIAK toutes les langues avec typage dynamique sont des langues interprétées. C'est déjà très inefficace, ajouter l'inefficacité de la frappe dynamique ne va pas être une grosse perte de temps. Un langage compilé, cependant, ne fera pas réellement référence à des choses par son nom lors de son exécution. (À l'exception de l'utilisation occasionnelle de la réflexion .net ou de fonctionnalités similaires qui sont très lentes par rapport au langage sous-jacent.) La recherche de tous ces noms va être lente, lente, lente.


3
Tu sais mal. Il existe de nombreux compilateurs pour les langages à typage dynamique, et certains d'entre eux sont assez rapides. Les exemples incluent Common Lisp, Scheme et Erlang.
Ryan Culpepper

3
Il n'y a pas de "langage interprété". Une langue est un ensemble abstrait de règles mathématiques. Une langue n'est ni compilée ni interprétée. Une langue est juste. La compilation et l'interprétation sont des traits de la mise en œuvre, pas le langage. Chaque langue peut être implémentée avec un interpréteur et chaque langue peut être implémentée avec un compilateur. Et à peu près tous les langages ont à la fois des implémentations interprétées et compilées, par exemple il y a des interprètes pour C et des compilateurs pour ECMAScript, Ruby et Python.
Jörg W Mittag

-4

Les langages typés dynamiquement sont souvent présentés comme "orientés objet". Ils ne sont pas. Ils peuvent être orientés vers l'encapsulation, mais jamais orientés objet. L'orientation des objets concerne les types.

"Le garçon conduit le vélo de son frère à l'épicerie et achète une miche de pain à l'épicier". En utilisant l'orientation objet, on peut immédiatement écrire un ensemble de classes (types) pour décrire ce scénario du monde réel.

Dans un langage à typage dynamique, le scénario ne pouvait être représenté que de la manière suivante:

"L'objet monte l'objet de son objet vers l'objet et achète un objet de l'objet".

Le pouvoir de l'orientation des objets réside dans sa capacité à modéliser le monde en termes naturels, de sorte que le développeur de logiciels puisse utiliser les deux côtés du cerveau pour écrire des logiciels et résoudre des problèmes davantage en tant qu'être humain, moins en tant que programmeur informatique. Ce pouvoir est absent dans les langages typés dynamiquement.

Le typage statique permet une meilleure efficacité de codage, une réutilisation et une maintenabilité, car les environnements de développement intégrés connaissent les types de variables. Connaissant les types de variables, un IDE peut fournir une complétion automatique afin que le programmeur n'ait pas à se référer à la définition de classe afin de se rappeler si la propriété du membre a été orthographiée "backlightControl" ou "backLightControl" ou "bkLightCtrl".

Le typage statique permet une refactorisation automatisée, car l'EDI connaît chaque endroit où une variable contient une instance de l'objet en cours de refactorisation.

Le typage statique permet une plus grande réutilisation et maintenabilité. La frappe dynamique est meilleure pour le code jetable. Supposons qu'un nouveau développeur arrive dans la rue et examine un morceau de code existant. Si le code est typé statiquement, le développeur peut, en deux clics de souris, examiner la définition de classe de chacune des variables impliquées, sait à quoi sert la classe, sait quelles autres méthodes et propriétés sont disponibles. Si le code est tapé dynamiquement, le développeur doit utiliser la recherche globale pour comprendre ce qui se passe.


2
Je crains de devoir être en désaccord avec vous sur la première partie de votre argument. Les langages à typage dynamique sont généralement "orientés objet", mais pas "orientés classe". Cette distinction est importante. Dans votre exemple, vous vivez réellement dans une illusion. Vous pouvez avoir un Boycours, mais je doute qu'il puisse faire tout ce qu'un "garçon" du monde réel fait. Cependant, dans votre exemple dynamique (l'objet roule ...), nous savons la seule chose importante à propos de cet objet "garçon" - il peut rouler . Telle est la philosophie de base des langages de type dynamique. Il a + ve s et -ve s. Lequel vous aimez est votre opinion.
Chip
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.