|

Écrivez
à AILES ! |

Retour
vers programmation |

Retour
vers les questions Langage |

Retour
vers les questions Type |
|
|
Questionnaire Langage de Programmation
Les Types
[AnnotedC++], [schupp], [eiffel]
|
Les types jouent un rôle de base
dans la définition de plusieurs paradigme
de programmation, dont le paradigme objet.
Le questionnaire objet sur la différence entre les notions de type,
classe et objet répond à des
questions encore plus tordues ("qu'est le type d'un
objet, le type d'une classe ?").
Ici, nous nous contenterons de revenir aux
catégorisations de base du type. |
D'où viennent les types ?
Du monde du hardware,
nous rappelle [eiffel],
puisque dès les
premiers programmes, le matériau de base des unités centrales
étaient de simples suites binaires (00101100011100 etc..) réparties
sur n bytes (avec un byte valant 6, 7 ou, le plus souvent de nos jours,
8 bits, cf. bits).
La caractéristique
que représente un type est issue d'une abstraction qui a
débutée au plus bas niveau, en décidant si la suite binaire
représente un entier, un flottant, une chaîne de caractères, etc...
Déclarer un type n'a pas toujours constitué la
seule solution. [eiffel]
rappelle qu'il a existé (et existe toujours ?) des
architectures dite "tagguées",
dans laquelle le hardware se sert d'une la valeur binaire manipulée
pour déduire le type. Mais à la moindre erreur de programmation, le
comportement était difficilement prévisible. Cette solution est
équivalente au typage dynamique, sauf que dans le
cas de l'architecture tagguée, c'est le hardware qui faisait la
vérification de type et nom le software.
Pourquoi typer ?
Pour répondre à cette question, il faut
considérer les 3 catégories de binding
qui existent : statique, dynamique et inféré.
Les enjeux restent :
- l'exactitude du logiciel
("software correctness") On sait dire, dès la compilation
ou lors de l'exécution, si le programme manipule les bonnes données
(c.à.d. du bon type) aux bons endroits;
- la lisibilité (avec ce que cela
suppose de facilité de débugguage, de modification, de maintenance).
Du coup, on se rend compte que le typage n'est pas
a proprement parlé "indispensable" à la programmation : on
peut très bien programmer sans jamais rien typer... à condition de
ne jamais se tromper!
Ainsi, je peux déclarer ;
x = 2 ;
f(x);
Le compilateur générera le code de façon à, lors de l'exécution
du programme, allouer à x l'espace mémoire d'un entier, puis à
passer cet espace à la fonction f... même si elle attendait une
adresse (codé, dans ce langage, sur 4 bytes au lieu de 2!). Seul le binding
de valeur est utilisé (pour lié x à 2),
mais aucun binding
de type (assignation de type) n'est, dans
l'absolu, nécessaire!... Mais c'est risqué!
Les Types :
On distingue 2 sortes de "types" (ou caractéristique)
(d'après
[AnnotedC++]):
-
les types de base (anglais : "fundamental
type"). Ce sont des caractéristiques qui ne peuvent être
décomposées, comme les entiers ("int"), les flottants
("float", "double", ...) ou les
"char". cf. primitive type.
-
les types dérivés,
qui se construisent d'une façon ou d'une autre à partir des type
de base.
Le plus connu de ces types dérivés est la classe
qui se construit autours de type de base ou d'autres types
dérivées au travers de :
- méthodes (via une fonction qui renvoie un type après en avoir
pris en argument) ;
- attributs (via des tableaux, pointeurs, des références, des
constantes, des types de base ou d'autres classes, etc...)
Pour être plus précis,
[schupp]
rappelle dans son cours "Data Type" les 5 catégories de
type :
-
Primitive
types (simple types, non-aggregates) : type primaires,
simple, non-agrégé, donc les types de base déjà mentionnés ci-dessus. Ils ont une :
- structure simple ne contenant qu'une seule valeur (integer,
character, float, bool)
- structure séquentielle simple (string vu comme un tableau de
char).
Les catégories qui suivent sont des types dérivés.
-
User-defined
ordinal types types
ordinaux défini par l'utilisateur dont le domaine comporte des
éléments que l'on peut compter et dont chaque élément (à
l'exception du premier et du dernier) ont un successeur et prédécesseur
défini.
Ces types incluent :
- l'enum.
- le sous-ensemble (subrange) comme l'int, le boolean, le character.
-
Aggregates
(les types agrégats) : ces types se construisent en agrégeant
d'autres types. Les langages fournissent des constructeurs de type
pour construire ces agrégats, dont on connaît au moins 5
représentants classiques :
- Array, Vector ;
- Record ;
- Pointer ;
- Set ;
- Union.
-
Abstract
data types (type abstrait) : ces types sont les plus
proches des classes : ils permettent de s'abstraire des types
précédents, qui ne font que représenter le hardware (int, bool,
etc...) d'une façon simple, énumérée ou groupée.
En introduisant ce niveau d'abstraction, on introduit
naturellement une encapsulation
car un type abstrait est une capsule englobant des types concrets
et des opérations qui leur sont associées. Si l'on associe à ce
type abstrait un mécanisme de visibilité (publique, protégé,
privé, ...), on complète cette abstraction avec du masquage d'information.
Dans le monde de la programmation objet, type et classe ont des
responsabilités différentes, comme le précise l'article sur les
2 types d'héritages;
le type représentant avant tout l'interface publique de la
classe.
-
Parameterized
Types (Generics) (type paramétré ou
"générique") : la généricité est un paradigme
à part entière, différent du paradigme objet. Un type
générique est un type de donnée (potentiellement abstrait) qui
est paramétré. (en C++ : les "templates"). Il s'agit
là d'un autre niveau d'abstraction, qui a un objectif différent
de l'abstraction des types concrets, comme le fait les types
abstraits.
Ici, on est dans une relation "contenant - contenu" dont
le type générique permet de s'abstraire du contenu.
Compatibilité de type
4 mécanismes sont possibles pour affirmer que 2
types sont "compatibles"
( [schupp])
:
- leur nom respectif ;
- leur structure.
- leur relation d'"alias"
- leur relation d'inclusion.
Les 2 premiers moyens sont stricts quant à leurs critères de
compatibilités et n'admettent pas le moindre polymorphisme.
Dans le cas des noms, 2 types déclarés sous
le même nom (typage statique) sont déclarées
compatibles.
C'est facile à implémenter, sans ambiguïté, mais sans possibilité
d'alias.
Dans le cas des structures, il faut soigneusement
préciser les conditions d'équivalence (ordre des champs identique ou
nom ?, nom des champs identiques ou non ? etc.);
C'est flexible, parfois trop permissif
Dans le cas d'alias, le langage défini qu'un type
est un "alias" d'un autre, comme dans le cas du double,
alias de l'int (employer un double à la place d'un int, c'est
"pareil").
Ca reste souple sans être trop permissif, et cela fonctionne via des
polymorphismes ad-hoc, que sont la coercion et la surcharge,
mais aussi le polymorphisme "universel"
qui est paramétré.
Dans le cas de la relation d'inclusion, on tombe
dans le classique polymorphisme
universel d'inclusion de type, basé sur le sous-typage.
|