Le typage : présentation thématique et historique

Formation

En Semi-présenciel Paris

Prix sur demande

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Description

  • Typologie

    Formation

  • Méthodologie

    En semi-présentiel

  • Lieu

    Paris

Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.

Les sites et dates disponibles

Lieu

Date de début

Paris ((75) Paris)
Voir plan
7 Cité Paradis, 75010

Date de début

Consulter

Questions / Réponses

Ajoutez votre question

Nos conseillers et autres utilisateurs pourront vous répondre

À qui souhaitez-vous addresser votre question?

Saisissez vos coordonnées pour recevoir une réponse

Nous ne publierons que votre nom et votre question

Les Avis

Le programme

Introduction du cours

Ce tutoriel est un peu particulier, car il n'est pas destiné à vous enseigner une technique particulière, ou un langage de programmation particulier.

Il s'agit d'un tuto sur le typage, un concept utile et très répandu dans l'ensemble des langages de programmation. Je compte expliquer les raisons qui ont motivé son apparition, ses principes fondamentaux, ses différentes formes et pourquoi pas, vers la fin, quelques fonctionnalités avancées.

Ne vous attendez donc pas à ressortir de la lecture (ou pas) de ce tuto avec de nouvelles connaissances concrètes, à mettre en oeuvre pour l'écriture d'applications. Attendez-vous à réfléchir sur un sujet souvent présenté comme allant de soi, qui concerne la pratique de la programmation en général, et, dans le meilleur des cas, qui vous aura permis de comprendre un peu mieux votre propre pratique de la programmation.

Les exemples seront pris dans des langages très divers (que vous n'aurez probablement pour la plupart jamais vus, mais c'est rigolo de découvrir de nouvelles choses, non ? :p ), et j'ai un faible pour l'aspect historique des choses, même si la progression que suit le tuto est plus centrée sur la logique que sur la chronologie.

Tout est nombre, pour l'ordinateur

Les ordinateurs manipulent des nombres. Ils stockent des nombres en mémoire, font transiter des nombres par leurs circuits, et envoient des informations aux autres ordinateurs sous forme de nombres. C'est l'ère du numérique.

La programmation, c'est (entre autres) le fait de dialoguer avec l'ordinateur, afin de lui faire faire les opérations que l'on veut : on décide ce qu'on veut, on le traduit dans un langage compréhensible pour l'ordinateur, et il l'exécute (programmer, c'est donc essentiellement traduire de la pensée en code).

Comme c'est un ordinateur, il ne manipule que des nombres, et ces opérations se transforment donc forcément, à un moment ou un autre, en opérations sur des nombres. Quand on veut faire faire à l'ordinateur des additions, multiplications ou autres opérations mathématiques (il fait ça très bien :) ), ce n'est pas surprenant de savoir qu'il manipule des nombres. Mais quand on clique sur un bouton, qu'on choisit une couleur dans un logiciel de dessin ou qu'on écrit du texte dans un éditeur, est-ce que l'on conçoit clairement de quelle manière il va interpréter la chose en tant que nombre ? Non. Ce n'est d'ailleurs pas possible, parce que la manière dont est effectuée la traduction dépend de beaucoup de choses, que ce soit au niveau logiciel (encodage, format utilisé...) ou matériel (processeur, mémoire, etc.).

Quand tout est nombre pour le langage de programmation...

Les langages de bas niveau actuels, ainsi que les premiers langages de programmation sont très proches de la machine sur laquelle ils s'exécutent : les objets qu'ils manipulent sont, eux aussi, des nombres.

En Assembleur par exemple, la majorité des instructions consiste à modifier de manière arithmétique (en additionnant, multipliant, ou effectuant d'autres opérations de ce genre) le contenu des cases mémoire de l'ordinateur. Un exemple encore plus frappant est le Brainfuck, un langage dont les seules opérations (à part l'entrée-sortie) permettent soit de changer de case mémoire, soit de changer la valeur de la case actuelle (et de boucler tant que la case ne contient pas 0). Pour les curieux, voici une multiplication en Brainfuck :

[->[->+>+<<]>[-<+>]<<]>>>Des types pour plus de sûreté

Ces langages conviennent très bien quand il s'agit de manipuler des nombres (ils ont au départ été conçus pour des applications scientifiques), mais assez vite des problèmes se posent.
Mettons que vous ayez écrit une opération qui transforme une chaîne de caractères (une suite de nombres en mémoire) dans la même suite, mais où tous les caractères majuscules sont remplacés par leurs équivalents minuscules. Cela suppose de bien connaître la convention de conversion nombre <-> caractère de votre langage, mais ce n'est sans doute pas un problème pour vous.

Imaginez maintenant que vous appliquiez par erreur cette opération non pas sur une phrase, mais sur la liste des âges des membres de votre site. L'ordinateur n'y verra que du feu : il traduira cette liste en une liste assez ressemblante, mais dans laquelle, par exemple, toutes les personnes âgées de 65 à 90 ans se seront vues vieillies de 32 ans ! Heureusement que les visiteurs de votre site sont plutôt jeunes...

Cette erreur est assez courante, et même si elle peut être évitée en faisant attention, elle peut être gênante. Imaginez qu'au lieu de la liste des âges des membres, vous ayez mis la liste de vos mots de passe, voire la liste des clés de contrôle du réacteur nucléaire ( :pirate: ) !

Peut-on l'éviter ?

Cette erreur est aisément repérable : un humain, si on lui demande de transformer en lettres majuscules une liste d'âges, va comprendre immédiatement qu'il y a quelque chose qui cloche. Pourquoi l'ordinateur ne pourrait-il pas faire pareil ?
C'est ce que se sont demandés très vite les ingénieurs concevant les ordinateurs et les langages de programmation, et ils ont vite décidé de mettre en place une gestion simple de ce genre d'erreur dans ces langages.

L'idée intuitive est qu'une lettre et un âge, ce "n'est pas la même chose". Comme vous l'a peut-être dit un jour votre professeur de physique, "on ne peut pas ajouter des patates et des carottes". On a donc introduit une sorte de classification des objets que manipule le langage : à chaque valeur du langage, on associe une étiquette qui dit "c'est une lettre", "c'est un âge" (ou "c'est un nombre"), "c'est une liste de lettres suivie par deux nombres entiers, puis un nombre à virgule", etc.
Cette étiquette s'appelle le type : le type d'une valeur est tout simplement le groupe d'objets auxquels il appartient.

Ensuite, on précise quand on déclare une variable à quel type elle appartient, et, quand on définit une opération (par exemple, la mise en minuscules), on précise (dans le code source du programme) sur quel type de données elle a le droit d'agir.

Alors, il suffit de mettre en place, pendant que l'ordinateur lit le texte de votre programme en prévision de son exécution, une étape très simple qui vérifie que chaque opération est bien utilisée sur les variables du type qui convient, et sinon de rapporter l'erreur au programmeur.

Le typage, c'est sémantique

La réaction des informaticiens face aux erreurs de ce genre (que l'on appelle depuis les erreurs de typage) est naturelle. Si vous aviez été ingénieur chez IBM dans les années 50, vous auriez sûrement réagi pareil. :p

Ce qui est intéressant, c'est que la démarche du typage s'inscrit dans une direction plus générale, que l'on peut observer en de nombreux endroits de l'histoire des langages de programmation : on a apporté du sens au langage.

Quand vous concevez votre programme de mise en minuscules, vous savez très bien quel type de variables vous allez manipuler. L'ordinateur, lui, ne le savait pas. En introduisant le typage, on a permis au programmeur de rajouter des informations (dans le code source), pour en quelque sorte augmenter les connaissances de l'ordinateur au sujet du programme. Plus l'ordinateur est au courant, mieux il peut vérifier que ces informations sont cohérentes, et vous avertir si cela n'est pas le cas.
Le fait de modéliser le sens des objets manipulés par un programme est généralement appelé de la sémantique.
La plupart des langages informatiques que vous connaissez se sont préoccupés de sémantique (de la manière dont les programmes qu'on pouvait écrire reflétaient bien la signification des actions voulues par le programmeur). Le C (créé pour être portable, et donc avoir des instructions reflétant mieux (qu'en assembleur) le sens des fonctions désirées, en faisant abstraction de l'architecture matérielle de l'ordinateur) et le XHTML (dont une des grandes avancées a été de mettre l'accent sur les balises décrivant la structure des pages web - et donc leur sens -, au lieu de leur présentation) en sont de bons exemples.

Cela ajoute effectivement du sens au programme

Regardez le code de la fonction qui met en minuscule un caractère majuscule, en C :

char tolower(char c) { if (c >= 'A' && c <= 'Z') return (c - 'A' + 'a'); else return c; }

Si la lettre est comprise entre 'A' et 'Z', on la décale dans les minuscules (ce n'est pas grave si vous ne comprenez pas pourquoi ça marche, et d'ailleurs ça ne marchera pas forcément, car cela repose sur des fonctionnalités qui dépendent de l'environnement), sinon on la renvoie inchangée.

Si un informaticien lit ce code, il comprendra immédiatement ce que fait cette fonction. Mais un des intérêts du typage repose dans le fait qu'en pratique, on n'est pas obligé de lire tout le code !
En effet, il est possible en C d'insérer avant le code d'une fonction un prototype, qui la déclare à l'avance, et qui est souvent placé dans les fichiers de header (.h) :

char tolower(char )

Ce qui est surprenant, c'est que cette ligne suffit pour comprendre ce que fait la fonction tolower ! Évidemment, cela suppose de connaître l'anglais, mais une fois que vous voyez le type (la fonction prend un char et renvoie un char), la signification est claire (de toute façon, en 1950, les ingénieurs d'IBM parlaient tous anglais :D ).
Ainsi, le typage a effectivement apporté du sens dans le code : on peut se contenter de lire le prototype d'une fonction pour la comprendre ; c'est un gros gain de temps par rapport à l'époque où il fallait chercher le code source de la fonction dans tout le programme (bon, la documentation ça existe, mais les vrais hommes (et femmes !) chez IBM lisaient plutôt le code source, évidemment).

... mais ce n'est pas si nouveau !

Si vous avez déjà fait de la physique, vous avez sûrement remarqué l'importance qu'attachent les physiciens aux unités des valeurs qu'ils manipulent : pas question d'ajouter des mètres et des kilogrammes ! Cette restriction, cette classification des valeurs a de nombreux avantages : entre autres, elle permet d'obtenir très vite des informations sur une grandeur (c'est une distance, c'est une température...), et elle réduit les risques de faire des erreurs de calcul : il est très simple de vérifier qu'une formule est bien typée (on dit qu'elle est "homogène"), et si ce n'est pas le cas on sait tout de suite que la formule est fausse !
C'est un peu comme cela qu'on utilise en général les types en informatique.

Langages dynamiques, langages statiques

La description que j'ai faite du processus de vérification du typage est plus que simpliste. En fait, il existe grosso-modo deux manières répandues de procéder : le typage statique, et le typage dynamique.

La différence réside dans le moment où la vérification est effectuée : au moment où les opérations sont effectuées, ou plutôt une fois pour toutes, avant de lancer le programme ?

Un langage typé dynamiquement : Python

Le principe du typage dynamique, c'est de typer au besoin, pendant l'exécution du programme : on fait ce que demande le programme, et on vérifie à chaque opération que le type correspond bien (ce qui peut se faire assez rapidement si le système des types est simple).
C'est le typage le plus utilisé par les langages de script.
Voici un exemple en Python :

def essai(test): if test: return (1 + 2) else: return (1 + "essai")

On a défini une fonction qui prend un booléen en argument, et renvoie (1 + 2) s'il est vrai, et (1 + "essai") s'il est faux. Il y a une erreur de typage (on ne peut pas ajouter un entier et un mot !) mais l'interpréteur n'a pas protesté. On essaie ensuite d'utiliser la fonction :

Citation

>>> essai(True)
3
>>> essai(False)
TypeError: unsupported operand type(s) for +: 'int' and 'str'

essai(True) renvoie un résultat correct, et seul essai(False) renvoie une erreur. Cela veut dire que tant qu'on n'est pas entré dans la branche "else" du if, aucune vérification de typage n'a été faite sur son contenu : on a véritablement un typage dynamique.

Ce typage est très simple à comprendre et à mettre en oeuvre. Cependant, un de ses désavantages apparents ici est le fait que pour être sûr que les types du programme sont corrects, il faut tester tous les "embranchements" du code. Ici, il faut faire deux tests, mais pour peu que vous ayez plusieurs if et des conditions un peu compliquées, tester l'ensemble du code devient vite long et difficile (même s'il existe des outils pour tenter d'automatiser le processus).
Avec un typage statique, comme vous allez le voir, toutes les erreurs seront relevées avant l'exécution du code.

Un langage typé statiquement : C

Un langage est typé statiquement si, avant de s'exécuter, votre code source passe par une phase de vérification des types. Si le langage est compilé, cette phase est souvent comprise dans le compilateur, mais il existe aussi des langages interprétés (ou des implémentations interprétées de langages, plus exactement) qui ont un typage statique.

L'exemple que j'ai choisi est le C. Ce langage n'a pas un système de types très évolué (il n'empêche pas beaucoup de choses), mais cela reste un typage statique, et il a inspiré les typages des langages qui l'ont suivi (le C++, principalement). De plus, indépendamment des contraintes de typage imposées par le langage, les compilateurs ont développé des techniques de recherche d'erreurs probables (si le code est valide, mais assez bizarre pour être une erreur dans la plupart des cas), qui donnent lieu à des avertissements, et peuvent donc être considérées comme une sécurité supplémentaire au niveau du typage.

Un exemple de problème de typage en C est la comparaison d'un entier de type "unsigned int" (un type qui ne peut contenir que des entiers positifs) et "int" (ou "signed int"), qui peut représenter des nombres négatifs.

#include <stdio.h> const unsigned int i = 23; const int j = -5; int main(void) { if (i < j) printf("i < j\n"); else printf("i > j\n"); return 0; }

Si vous compilez ce code, vous verrez peut-être (si vous avez les warning activés) un message équivalent à celui-ci :

Citation

test.c: In function 'main':
test.c:8: warning: comparison between signed and unsigned

Le compilateur a repéré l'erreur de type, mais il compile le programme quand même. Si vous l'exécutez, vous verrez qu'il dit que i est plus petit que j !
23 < -5, ça ne ferait pas plaisir à votre prof de maths, mais c'est normal si l'on connaît les règles de conversion utilisées pour passer d'un int à un unsigned int.

L'avantage du typage statique, c'est qu'il nous prévient de l'erreur avant l'exécution. De plus, il vérifie tout le code, même celui qui se trouve dans des branches de "if".
Le typage statique présente aussi un avantage non négligeable du point de vue des performances : une fois que le test de typage a été effectué, le programme est supposé valide, et le compilateur peut alors se débarrasser des informations de typage qu'il a accumulées. À l'inverse, les typages dynamiques imposent de conserver pendant toute l'exécution les informations sur le type de chaque variable, et de faire des tests tout au long du programme, ce qui a un impact sur les performances.

Tout est dans la nuance

Dans le monde du typage, rien n'est tout blanc ni tout noir. Tout est dans la nuance : il existe un grand nombre de manières différentes de typer, et tous les langages font ça un peu différemment, avec plus ou moins de contraintes. Ainsi, même les langages que l'on désigne couramment comme "non typés" (comme Lisp) ont généralement une certaine forme (très réduite) de typage.

De même, aucun langage à ma connaissance n'est complètement rigoureux au niveau du typage. Par exemple, l'opération de division / peut s'effectuer sur des entiers, et provoque une erreur quand le deuxième argument est nul (division par zéro). Dans un langage complètement typé (au sens mathématique du terme), il faudrait définir un type "entier non nul" qui serait celui du deuxième argument de la division, et il faudra convertir, et n'autoriser que des variables de ce type (ou faire une conversion implicite).

Le typage se mesure donc en degrés, en niveaux, et assez approximativement. Les langages les plus typés actuellement sont désignés comme pratiquant un typage...

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Le typage : présentation thématique et historique

Prix sur demande