Site WWW de Laurent Bloch
Slogan du site

ISSN 2271-3905
Cliquez ici si vous voulez visiter mon autre site, orienté vers des sujets moins techniques.

Pour recevoir (au plus une fois par semaine) les nouveautés de ce site, indiquez ici votre adresse électronique :

Pourquoi l’IA ne signe-t-elle pas la fin de la programmation ?
Deux articles de Matt Welsh et de Daniel Yellin pour les CACM
Article mis en ligne le 7 mars 2023
dernière modification le 19 mai 2024

par Laurent Bloch

En 1969 j’ai débuté en informatique, d’abord comme analyste-programmeur, puis comme ingénieur système, et deux ou trois ans plus tard, il y a donc un demi-siècle, j’ai lu les premiers articles qui annonçaient la disparition de ces métiers, qui non seulement existent toujours, mais pour lesquels l’offre d’emploi augmente chaque année. Quels sont les arguments à l’appui de la thèse de leur disparition ? Les ordinateurs seraient de plus en plus perfectionnés, et même « intelligents », on pourrait leur « parler », ils « comprendraient » nos « besoins » et sauraient réaliser le programme nécessaire à leur satisfaction. Tous ces arguments relèvent de l’intelligence artificielle, discipline elle-même sujette à des hauts et des bas. La thèse de la fin de la programmation ressort périodiquement, s’évanouit temporairement, puis connaît une résurgence : il semble que nous soyons aujourd’hui dans une telle phase de résurgence, stimulée par des résultats spectaculaires obtenus avec le système d’intelligence artificielle ChatGPT, qui permet effectivement de créer des programmes. Dans certaines conditions.

Dans un article précédent consacré au décodage de logiciels de cyberattaque j’expliquais pourquoi, dans ce cas particulier, une compétence solide en science de la programmation était indispensable. Mais il fallait une réfutation plus générale de la thèse idiote de la fin de la programmation.

Les promesses de ChatGPT

ChatGPT m’avait plongé dans une certaine perplexité, lorsqu’arriva sous mon paillasson le numéro de janvier 2023 des Communications of the Association for Computing Machinery (CACM), l’organe de la principale société savante informatique, avec un article intitulé justement The End of Programming, signé de Matt Welsh, un homme qui après un doctorat à Berkeley fut professeur d’informatique à Harvard, puis directeur de l’ingénierie chez Google et chez Apple : je me suis dit que d’une plume si compétente j’allais enfin apprendre le fin mot de l’histoire [1]. Hélas, rien que les fadaises habituelles, dont il faut d’ailleurs remarquer qu’elles transmettent une certaine part de vérité : il est exact qu’aujourd’hui une grande partie de la programmation en entreprise consiste à assembler dans des frameworks des templates et des programmes de bibliothèque, et que les environnements de programmation par objets permettent au programmeur de simplement déclarer une classe d’objets, dont le système produira ensuite toute les méthodes. C’est très puissant, cela accroît considérablement la productivité, à un prix : personne ne sait plus très bien qui a écrit le programme. En tout état de cause, ce n’est pas de la programmation par l’ordinateur, c’est de l’utilisation de programmes écrits par d’autres.

ChatGPT ne fait pas autre chose, mais il le fait à une échelle grandiose : le système explore les dépôts publics de logiciels tels que GitHub, il interroge le site fameux Stack Overflow [2], il effectue quelques analyses plus ou moins subtiles, et il donne un programme, copié sur les réponses les plus fréquentes aux millions de requêtes qu’il a effectuées.

Les résultats peuvent être impressionnants, mais on peut d’emblée adresser un premier reproche à cette méthode de programmation : si elle est apparemment économe en compétences humaines, elle est extrêmement coûteuse en matériel et en énergie. La lecture et l’analyse (même sommaire) de millions de programmes mobilise des moyens de calcul considérables, lesquels à leur tour consomment énormément d’énergie électrique (sans parler de l’empreinte immobilière). Microsoft a dû investir dix milliards (oui, milliards) de dollars pour que ChatGPT donne ses résultats spectaculaires.

La seconde critique qui vient tout de suite à l’esprit : on ne peut ainsi rien produire de vraiment nouveau, on ne fait que copier plus ou moins intelligemment des programmes déjà écrits.

Heureusement, le numéro suivant des CACM devait apporter aux allégations du professeur Welsh une réplique circonstanciée.

Huit bonnes raisons de continuer la programmation

Dans ce numéro de février 2023 des CACM, Daniel Yellin, vice-président d’IBM Cloud et Distinguished Engineer at IBM, donne son point de vue sur The Premature Obituary of Programming (Une nécrologie prématurée de la programmation). Je note qu’il ne mentionne ni ChatGPT ni le point de vue de Matt Welsh, mais qu’il aborde de façon plus générale le deep programming (DP), enfant plus ou moins légitime du deep learning (DL), en étudiant ses deux représentants les plus en vue, Codex d’OpenAI, utilisé par le projet Copilot de GitHub, et AlphaCode de Deepmind. Ces réalisations ne sont pas sans proximité avec ChatGPT, il s’agit manifestement du même courant de pensée.

Daniel Yellin admet tout à fait que les millions de lignes de code des dépôts de logiciel libre emmagasinent quantité de méthodes de programmation de nature à résoudre quantité de problèmes. De problèmes qui ont déjà été posés et résolus. Ce qui doit aider à résoudre de nouveau le même problème, ou un problème de la même famille dans un environnement différent. Mais l’auteur nous soumet une liste d’objections, qui énoncent autant de situations où il faudra bien se mettre à programmer de novo :

 L’informatique et ses applications évoluent, entre autres, par la création d’architectures nouvelles de machines et de réseaux, qui imposent de nouvelles méthodes de programmation. Que l’on pense seulement à l’exemple récent de l’infonuagique...

 De nouvelles familles de problèmes nécessitent des efforts de programmation novateurs, citons seulement l’Internet of Things (IoT).

 Le monde réel change et pose de nouveaux problèmes non encore programmés, comme les révolutions du monde biomédical ou le changement climatique...

 Les modèles issus de la « programmation profonde » (DP) sont d’une taille peu soutenable, l’auteur cite un modèle à 137 milliards de paramètres. Certaines applications ne justifient pas les investissements colossaux induits par de telles solutions, et relèveront toujours (ou du moins longtemps) de méthodes différentes.

Daniel Yellin énonce également des objections d’une autre nature, plus intrinsèques à l’activité de programmation :

 L’hypothèse implicite qui justifie l’idée de la fin de la programmation est qu’il serait plus facile de spécifier un programme en langage « naturel » que de l’écrire au moyen d’un langage de programmation. Edsger Dijkstra a réfuté cet argument : le langage humain est trop imprécis et trop ambigu pour spécifier un programme de quelque complexité. De façon contre-intuitive, spécifier un programme pour un calcul de physique est plus simple que pour une application de gestion du personnel, parce que la programmation pour la physique peut s’appuyer sur un corpus robuste et contraignant : les lois de la physique, alors qu’il n’existe rien d’équivalent pour la gestion du personnel, laissée aux appréciations plus ou moins subjectives d’humains faillibles.

 Les erreurs de programmation sont souvent idiotes, mais néanmoins très difficile à détecter : un caractère modifié dans un texte de milliers de lignes peut tout corrompre, et corrompre est plus grave que détruire.

 La programmation est une activité sociale : les développeurs discutent entre eux à la cantine et à la machine à café, et c’est souvent leur activité la plus fructueuse.

 Si la « programmation profonde » (DP) remplace de plus en plus la programmation traditionnelle, plus ou moins artisanale, cela signifie que de plus en plus de programmes seront des copies de programmes antérieurs, la créativité s’assèchera, le « capital génétique » de la population des programmes s’appauvrira, et il deviendra de plus en plus difficile de découvrir de nouveaux algorithmes, de nouvelles méthodes de programmation.

Daniel Yellin ne dénie pas tout intérêt à la programmation profonde : il indique simplement que la programmation par les programmeurs peut et doit subsister, que la programmation profonde peut lui apporter du renfort, que certains domaines y sont sans doute plus propices que d’autres, et que finalement cette collection de méthodes nouvelles ne fait que s’ajouter à l’arsenal déjà bien garni du développement informatique.

Petit exemple en Java avec ChatGPT

Un lecteur me demande d’écrire en Java un programme qui demande à l’utilisateur de saisir son nom, son prénom et son âge et qui affiche s’il est majeur. Voici la réponse de ChatGPT :

Voici un programme Java simple qui demande à l’utilisateur de saisir son nom, son prénom et son âge, puis affiche un message indiquant s’il est majeur ou non. Un utilisateur est considéré comme majeur s’il a 18 ans ou plus.

  1. import java.util.Scanner;
  2.  
  3. public class MajeurChecker {
  4.     public static void main(String[] args) {
  5.         // Création de l'objet Scanner pour lire l'entrée de l'utilisateur
  6.         Scanner scanner = new Scanner(System.in);
  7.        
  8.         // Demande du nom de l'utilisateur
  9.         System.out.print("Veuillez saisir votre nom : ");
  10.         String nom = scanner.nextLine();
  11.        
  12.         // Demande du prénom de l'utilisateur
  13.         System.out.print("Veuillez saisir votre prénom : ");
  14.         String prenom = scanner.nextLine();
  15.        
  16.         // Demande de l'âge de l'utilisateur
  17.         System.out.print("Veuillez saisir votre âge : ");
  18.         int age = scanner.nextInt();
  19.        
  20.         // Vérification de la majorité
  21.         if (age >= 18) {
  22.             System.out.println(prenom + " " + nom + ", vous êtes majeur.");
  23.         } else {
  24.             System.out.println(prenom + " " + nom + ", vous êtes mineur.");
  25.         }
  26.        
  27.         // Fermeture du scanner
  28.         scanner.close();
  29.     }
  30. }

Télécharger

Explication du code

 Importation de la classe Scanner : import java.util.Scanner; permet d’utiliser la classe Scanner pour lire les entrées de l’utilisateur depuis la console.

 Création de l’objet Scanner : Scanner scanner = new Scanner(System.in); crée un nouvel objet Scanner pour lire les données entrées par l’utilisateur.

 Lecture du nom : String nom = scanner.nextLine(); lit une ligne de texte entrée par l’utilisateur et la stocke dans la variable nom.

 Lecture du prénom : String prenom = scanner.nextLine(); lit une autre ligne de texte et la stocke dans la variable prenom.

 Lecture de l’âge : int age = scanner.nextInt(); lit un entier entré par l’utilisateur et le stocke dans la variable age.

 Vérification de la majorité : if (age >= 18) { ... } else { ... } vérifie si l’âge est supérieur ou égal à 18 et affiche le message correspondant.

 Fermeture du scanner : scanner.close(); ferme l’objet Scanner pour libérer les ressources associées.

Ce programme simple couvre les bases de la saisie utilisateur et de la conditionnelle en Java. Assurez-vous d’exécuter ce programme dans un environnement Java approprié pour voir les résultats.

Le même en Scheme et en OCaml

Voici un programme en Scheme qui demande à l’utilisateur de saisir son nom, son prénom et son âge, puis affiche un message indiquant s’il est majeur ou non. Ce programme utilise les fonctions d’entrée et de sortie standard en Scheme.

  1. (define (read-line)
  2.   (let ((line (read-string)))
  3.     (if (eof-object? line)
  4.         (begin
  5.           (display "Fin de l'entrée.")
  6.           (newline)
  7.           "")
  8.         line)))
  9.  
  10. (define (read-integer)
  11.   (let ((line (read-string)))
  12.     (if (eof-object? line)
  13.         (begin
  14.           (display "Fin de l'entrée.")
  15.           (newline)
  16.           0)
  17.         (string->number line))))
  18.  
  19. (define (majeur-checker)
  20.   (display "Veuillez saisir votre nom : ")
  21.   (let ((nom (read-line)))
  22.     (display "Veuillez saisir votre prénom : ")
  23.     (let ((prenom (read-line)))
  24.       (display "Veuillez saisir votre âge : ")
  25.       (let ((age (read-integer)))
  26.         (if (>= age 18)
  27.             (begin
  28.               (display prenom)
  29.               (display " ")
  30.               (display nom)
  31.               (display ", vous êtes majeur.")
  32.               (newline))
  33.             (begin
  34.               (display prenom)
  35.               (display " ")
  36.               (display nom)
  37.               (display ", vous êtes mineur.")
  38.               (newline)))))))
  39.  
  40. (majeur-checker)

Télécharger

Explication du code

 Définition de read-line :
La fonction read-line lit une ligne de l’entrée standard. Si la fin de l’entrée est atteinte, elle affiche un message et retourne une chaîne vide.

 Définition de read-integer :
La fonction read-integer lit une ligne de l’entrée standard et la convertit en nombre. Si la fin de l’entrée est atteinte, elle affiche un message et retourne 0.

 Définition de majeur-checker :
La fonction principale majeur-checker demande à l’utilisateur de saisir son nom, son prénom et son âge.
Elle utilise read-line pour lire les chaînes de caractères et read-integer pour lire l’âge.
Elle vérifie si l’âge est supérieur ou égal à 18 et affiche le message approprié.

 Appel de la fonction majeur-checker :
Cette ligne lance le programme.

Ce programme utilise des fonctions de base pour lire les entrées de l’utilisateur et effectuer des opérations conditionnelles en Scheme. Assurez-vous d’exécuter ce programme dans un interpréteur Scheme qui supporte les fonctions d’entrée/sortie standard, comme Racket ou MIT/GNU Scheme.
Écrire un programme en OCaml qui demande à l’utilisateur de saisir son nom, son prénom et son âge et qui affiche s’il est majeur :
ChatGPT

Voici un programme en OCaml qui demande à l’utilisateur de saisir son nom, son prénom et son âge, puis affiche un message indiquant s’il est majeur ou non. Ce programme utilise les fonctions d’entrée et de sortie standard en OCaml.

  1. (* Fonction pour lire une ligne de l'entrée standard *)
  2. let read_line () =
  3.   try
  4.     input_line stdin
  5.   with End_of_file -> ""
  6.  
  7. (* Fonction pour lire un entier de l'entrée standard *)
  8. let read_int () =
  9.   try
  10.   with
  11.   | Failure _ -> 0
  12.   | End_of_file -> 0
  13.  
  14. (* Fonction principale *)
  15. let majeur_checker () =
  16.   (* Demander le nom *)
  17.   print_string "Veuillez saisir votre nom : ";
  18.   let nom = read_line () in
  19.  
  20.   (* Demander le prénom *)
  21.   print_string "Veuillez saisir votre prénom : ";
  22.   let prenom = read_line () in
  23.  
  24.   (* Demander l'âge *)
  25.   print_string "Veuillez saisir votre âge : ";
  26.   let age = read_int () in
  27.  
  28.   (* Vérifier si l'utilisateur est majeur *)
  29.   if age >= 18 then
  30.     Printf.printf "%s %s, vous êtes majeur.\n" prenom nom
  31.   else
  32.     Printf.printf "%s %s, vous êtes mineur.\n" prenom nom
  33.  
  34. (* Appeler la fonction principale *)
  35. let () = majeur_checker ()

Télécharger

Explication du code

 Fonction read_line :
Cette fonction lit une ligne de l’entrée standard et retourne une chaîne de caractères. Elle gère l’exception End_of_file en retournant une chaîne vide.

 Fonction read_int :
Cette fonction lit une ligne de l’entrée standard, essaie de la convertir en entier, et retourne cet entier. En cas d’échec de conversion (exception Failure) ou si la fin de l’entrée est atteinte (End_of_file), elle retourne 0.

 Fonction principale majeur_checker :
Cette fonction demande à l’utilisateur de saisir son nom, son prénom et son âge.
Elle utilise read_line pour lire les chaînes de caractères (nom et prénom) et read_int pour lire l’âge.
Elle vérifie si l’âge est supérieur ou égal à 18 et affiche un message approprié en utilisant Printf.printf.

 Appel de la fonction principale :
let () = majeur_checker () appelle la fonction principale pour exécuter le programme.

Pour exécuter ce programme, vous pouvez le copier dans un fichier avec l’extension .ml (par exemple majeur_checker.ml) et utiliser le compilateur OCaml pour le compiler et l’exécuter :

  1. ocamlc -o majeur_checker majeur_checker.ml
  2. ./majeur_checker

Télécharger

Ce programme simple couvre les bases de la saisie utilisateur, de la gestion des exceptions et des opérations conditionnelles en OCaml.

Et en Rust ?

Voici un programme en Rust qui demande à l’utilisateur de saisir son nom, son prénom et son âge, puis affiche un message indiquant s’il est majeur ou non. Ce programme utilise les fonctionnalités d’entrée/sortie standard de Rust.

  1. use std::io::{self, Write};
  2.  
  3. fn main() {
  4.     // Fonction pour lire une ligne de l'entrée standard
  5.     fn read_input(prompt: &str) -> String {
  6.         print!("{}", prompt);
  7.         io::stdout().flush().unwrap();  // Assurez-vous que le prompt s'affiche avant la lecture
  8.         let mut input = String::new();
  9.         io::stdin().read_line(&mut input).expect("Échec de lecture de l'entrée");
  10.         input.trim().to_string()
  11.     }
  12.  
  13.     // Demande du nom de l'utilisateur
  14.     let nom = read_input("Veuillez saisir votre nom : ");
  15.  
  16.     // Demande du prénom de l'utilisateur
  17.     let prenom = read_input("Veuillez saisir votre prénom : ");
  18.  
  19.     // Demande de l'âge de l'utilisateur
  20.     let age_str = read_input("Veuillez saisir votre âge : ");
  21.     let age: u32 = age_str.trim().parse().expect("Veuillez entrer un nombre valide");
  22.  
  23.     // Vérification de la majorité
  24.     if age >= 18 {
  25.         println!("{}, {}, vous êtes majeur.", prenom, nom);
  26.     } else {
  27.         println!("{}, {}, vous êtes mineur.", prenom, nom);
  28.     }
  29. }

Télécharger

Explication du code

 Importation des modules nécessaires :
use std::io::{self, Write}; importe les modules nécessaires pour la lecture et l’écriture de l’entrée/sortie.

- Fonction read_input :
Cette fonction affiche une invite (prompt), lit une ligne de l’entrée standard, supprime les espaces blancs en début et fin de chaîne, et retourne la chaîne de caractères résultante.
print!("{}", prompt); affiche le prompt.
io::stdout().flush().unwrap(); s’assure que le prompt est affiché avant de lire l’entrée.
let mut input = String::new(); crée une nouvelle chaîne mutable pour stocker l’entrée.
io::stdin().read_line(&mut input).expect("Échec de lecture de l'entrée"); lit une ligne de l’entrée standard.
input.trim().to_string() supprime les espaces blancs et retourne la chaîne.

 Lecture du nom, du prénom et de l’âge :
let nom = read_input("Veuillez saisir votre nom : "); lit le nom de l’utilisateur.
let prenom = read_input("Veuillez saisir votre prénom : "); lit le prénom de l’utilisateur.
let age_str = read_input("Veuillez saisir votre âge : "); lit l’âge de l’utilisateur sous forme de chaîne de caractères.
let age: u32 = age_str.trim().parse().expect("Veuillez entrer un nombre valide"); convertit la chaîne en nombre.

 Vérification de la majorité :
if age >= 18 { ... } else { ... } vérifie si l’âge est supérieur ou égal à 18 et affiche le message approprié.

Pour exécuter ce programme, vous pouvez le copier dans un fichier avec l’extension .rs (par exemple majeur_checker.rs) et utiliser cargo ou rustc pour le compiler et l’exécuter :

  1. rustc majeur_checker.rs
  2. ./majeur_checker

Télécharger

Ce programme couvre les bases de la saisie utilisateur et de l’opération conditionnelle en Rust.