par Laurent Bloch
Emmanuel Saint-James a pu écrire dans son livre La Programmation applicative (de Lisp à la machine en passant par le λ-calcul) que « la programmation est une transmission d’un raisonnement à une machine, capable de le reproduire. Par cette reproduction, sans laquelle il n’est point de science, l’ordinateur s’affirme comme un instrument d’objectivation du raisonnement. Il s’inscrit dans la lignée des appareils qui ont permis de passer de l’expérience empirique à l’expérimentation scientifique : l’informatique se distingue des mathématiques par un outil d’expérimentation permettant d’observer, vérifier, réfuter un raisonnement. »
Cette « transmission d’un raisonnement à une machine » s’effectue par le truchement d’un langage, qui est incorporé à la machine, stricto sensu, ce n’est pas une métaphore. L’ordinateur a un langage en lui. Les verbes de ce langage sont les instructions du processeur, qui effectuent des opérations (addition, déplacement d’une donnée, branchement à l’adresse d’une autre instruction...) sur les données (les substantifs du langage). Les instructions sont des objets physiques, en l’occurrence des circuits électroniques qui réalisent les opérations conformément aux règles de l’algèbre de Boole.
Les données sont enregistrées dans la mémoire (l’anglais storage, moins anthropomorphique, serait moins source de confusion) où elles sont repérées par leur adresse, qui est le numéro de case mémoire à partir du début. Le texte du programme, qui mentionne les instructions à effectuer et les données auxquelles elles s’appliquent, est lui aussi enregistré dans la mémoire, l’unité de contrôle du processeur (la locution unité de commande serait plus appropriée) parcourt ce texte et exécute les instructions selon la séquence convenable. Enregistrer le texte du programme dans la même mémoire que les données est l’idée la plus révolutionnaire de l’architecture inventée par John von Neumann. Comme l’a dit Samuel Goyet lors d’une séance du séminaire Codes sources, avant von Neumann, programmer c’était tourner des boutons et brancher des fiches dans des tableaux de connexion, depuis von Neumann c’est écrire un texte ; cette révolution ouvrait la voie à la science informatique.
On peut programmer en langage machine, le langage concret de l’ordinateur : cela consiste à écrire des textes dans l’alphabet {0, 1}. C’est difficile, laborieux, extraordinairement inefficace, et de plus chaque modèle d’ordinateur a son propre langage, qu’il faut apprendre à chaque fois. Aussi, depuis les années 1950, les informaticiens ont-ils dépensé une part considérable de leur énergie à créer des langages plus commodes, en fait des métalangages du langage machine, plus abstraits, moins liés aux caractéristiques physiques du processeur. Quand on a écrit un programme dans un de ces langages, afin qu’il puisse être exécuté, il faut le traduire en langage machine, le seul que le processeur puisse interpréter. Cette traduction est effectuée par un programme nommé compilateur. Il existe une hiérarchie de ces métalangages, du plus concret (proche du langage machine, « de bas niveau ») au plus abstrait (« de haut niveau »). Au bas de cette hiérarchie se trouvent les assembleurs, dont les instructions correspondent une pour une à celles du langage machine (il y a donc un assembleur différent pour chaque modèle de processeur), mais écrites plus commodément, avec des codes mnémoniques pour désigner les instructions et des noms symboliques pour désigner les adresses.
Plus on monte dans la hiérarchie des langages, plus on gagne en abstraction, c’est-à-dire que l’on a moins à se préoccuper du fonctionnement technique de l’ordinateur, et que les programmes peuvent fonctionner indifféremment sur des ordinateurs de types différents, mais cette abstraction a un prix qui se paie en efficacité. Et pour écrire les programmes du système d’exploitation, qui établissent le lien entre ce que perçoivent les programmeurs (voire les utilisateurs) et ce qui se passe dans les circuits, on aura toujours besoin de langages de bas niveau, tels que C, Rust ou les assembleurs.
Cette propriété de l’ordinateur, avoir un langage en soi, en fait une machine universelle. Qui plus est, la thèse de Church-Turing soutient que tous les ordinateurs, c’est-à-dire tous les automates programmables universels (capables d’effectuer le calcul de tout algorithme) sont équivalents. Cette thèse établit un lien entre la notion intuitive de calculabilité et les formalismes qui permettent d’exprimer des procédures effectives : λ-calcul, machines de Turing et de Post.
Un jour un chercheur de renom a établi, face à moi, un parallèle entre l’informatique et l’électrophorèse, à l’époque très utilisée pour le séquençage de l’ADN ; j’ai failli hurler de rage. Une autre fois ce fut le cryostat. On voit bien que cela n’a rien à voir : le cryostat, l’appareil d’électrophorèse font une chose, compliquée et utile à la science, mais une chose. Ce sont des appareils qui, à un moment donné, peuvent ou ont pu aider les chercheurs. L’ordinateur est un automate qui peut traiter tous les problèmes pour lesquels existent des solutions calculables (il y a des problèmes sans solution et des solutions incalculables). La programmation est l’art de réaliser des algorithmes pour calculer ces solutions. Entendons-nous bien : depuis Pascal et plus encore depuis Leibniz nous savons que le calcul ne se limite pas aux nombres, il s’étend à la logique, et Leibniz a été suivi sur ce terrain par Boole, Frege, Church et Turing, pour finalement donner naissance au versant théorique de l’informatique. Pour le versant pratique, que Pascal et Leibniz ont également parcouru, on nommerait plutôt von Neumann.
Voilà pourquoi l’ordinateur n’est pas (du tout) une machine comme les autres.