Témoignage
 

Rubrique « Info »

 



É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.


               
 
Avertissement !
 
Décollage !  |  Présentation du site web "AILES"  | 
Infos générales  |  articles "Informatique"