J'avais expliqué il y a quelques
mois que je m'étais engagé à monter un nouveau cours
d'informatique théorique (ou faut-il
dire de maths de l'informatique ?) pour certains élèves de
première année à
Télécom PlusÀParis,
intitulé Logique et Fondements de l'Informatique
(nom de code
interne : INF110
). (J'avais d'ailleurs publié un premier jeu
de transparents dans de billet.)
L'occurrence 2023–2024 de ce cours a eu lieu
de
à , le contrôle
le , et maintenant
(enfin, quelques mois après) que j'ai à la fois rendu les notes
finales et reçu les retours (anonymes) des étudiants je suis en mesure
d'en faire un premier bilan. L'idée de ce billet n'est pas tellement
d'entrer dans le fond du sujet (même si je ne peux pas ne pas en
parler du tout), à ce sujet voir plutôt les deux paragraphes
ci-dessous, mais de parler au niveau méta : pourquoi ce cours,
pourquoi ce sujet, comment je l'ai organisé et préparé, ce qui a bien
marché et ce qui n'a pas bien marché. Le but est de me permettre à
moi-même de faire le point, et donc de réfléchir à ce que je veux
changer l'an prochain, et aussi de préparer un exposé où je dois
raconter tout ça (cf. la fin du présent billet).
☞ Pour le fond du sujet, je renvoie notamment à ce billet sur la correspondance de Curry-Howard et à celui-ci sur la réalisabilité propositionnelle, qui sont largement adaptés de certains bouts de mon cours, même si la présentation est différente et qu'ils vont plus loin (surtout le second). Je peux aussi renvoyer à ce billet sur des sujets variés en vrac sur lesquels j'ai appris des choses en préparant ce cours. Enfin, je fais un lien vers ce billet sur les degrés de Turing et leur généralisation parce qu'il touche à des questions adjacentes, même si je n'ai pas fait plus qu'évoquer la définition de degré de Turing (ordinaire) dans mon cours.
☞ Pour ce qui est des documents pédagogiques accompagnant le cours, voici quelques liens (dont, malgré ma passion pour la stabilité des URL, je ne garantis pas qu'ils seront pérennes, parce que je ne sais pas comment je vais les réorganiser à l'avenir). D'abord le PDF des transparents (qui est la concaténation idiote de trois parties inégales : ① calculabilité, ② typage simple et calcul propositionnel et ③ introduction aux quantificateurs ; j'ai aussi fait une version « imprimable » des transparents : ①, ② et ③). Les exercices d'entraînement sont : ici sans corrigé et ici avec corrigé. Et le sujet du contrôle est ici sans corrigé et ici avec corrigé. Et si des gens sont intéressés par le source de tout ça, le dépôt Git est ici.
Avertissement usuel : même si j'évoque ici mon travail d'enseignant-chercheur à Télécom, je dois rappeler que, dans ce billet comme ailleurs sur ce blog, je m'exprime à titre purement personnel et que les avis et opinions que je peux exprimer ici (ou ailleurs) sont juste les miens et n'engagent en aucun cas mon employeur (ni qui que ce soit d'autre que moi, et d'ailleurs ils ne m'engagent même pas moi vu que que je me réserve le droit d'en changer à tout moment et sans prévenir). J'ajoute que si vous faites partie des personnes qui ont besoin d'un tel avertissement, il vous est interdit de lire la suite de ce billet.
Plan du billet
Le contexte
Comme je l'ai expliqué dans les billets déjà liés ci-dessus
(celui-ci
et celui-là), la France a récemment
créé une filière MPI
(maths, physique et
informatique) dans ses classes préparatoires
scientifiques[#]. En
particulier, les élèves entrant à Télécom par le concours commun
peuvent arriver avec trois niveaux de formation en informatique : le
socle commun MP/PC (où il n'y a pas
grand-chose en info), l'option informatique en MP (qui
existait déjà), et la nouvelle filière MPI (qui va encore
plus loin). Les programmes de toutes ces filières sont trouvables par
exemple sur ce
site). Ceci nous a imposé une réforme de nos enseignements en
première année : jusqu'à l'été 2023 notre première année était
complètement uniforme (en gros, tout le monde avait les mêmes cours,
le choix d'une filière de spécialisation se faisant en seconde année),
mais ce mode de fonctionnement devenait intenable, au moins pour ce
qui est de l'informatique, avec de telles différences de formation à
l'entrée (si on s'adapte à ceux qui en ont fait le moins, ceux qui en
ont fait plus s'ennuient, et si on s'adapte à ceux qui en ont fait le
plus, ceux qui en ont fait le moins sont largués).
[#] Au niveau de la
reconnaissance de l'informatique comme une science à part entière, il
y a aussi eu création d'une agrégation d'informatique pour le
recrutement des enseignants alors que l'informatique était jusqu'alors
considérée comme une option de l'agrégation de mathématiques. Et, un
peu avant, des changements au niveau lycée avec la création d'une
spécialité NSI, c'est-à-dire numérique et sciences
informatiques
, mais ça ne me concerne que beaucoup plus
indirectement : de ce que je comprends, à cause de la limitation à
deux spécialités en terminale, la (grande ?) majorité des élèves de
prépa, y compris MPI, n'ont pas suivi cette
spécialité NSI en terminale, mais bien maths et
physique-chimie.
Mais ce n'est pas, en fait, qu'une question de niveau en entrée : les élèves ayant choisi la filière MPI semblent avoir des attentes et des intérêts différents de ceux de la filière MP (même « option info »), au sens où ils semblent généralement plus intéressés par l'informatique pour elle-même et pas juste comme un outil dans un travail d'ingénieur (parce qu'évidemment tout le monde utilise l'informatique de nos jours). Et un des buts de la réforme que nous avons mise en place est de rentre Télécom plus attirante pour les préparationnaires vraiment intéressés par l'informatique, avec notamment l'idée qu'il ne faut pas qu'ils s'ennuient en cours.
La première année à Télécom est (maintenant) divisée en quatre périodes. Pour ce qui est de l'enseignement d'informatique, voici comment nous avons organisé l'année. La période P1 (septembre–octobre) a été dédiée à une introduction générale à la programmation, du matériel au haut niveau, tandis que les élèves venus de la filière MPI (et uniquement eux) ont un cours spécifique sur la compilation. La période P2 (novembre–janvier) est consacrée à la théorie de l'informatique, et c'est celle qui me concerne et dont je vais reparler. La période P3 (février–avril) propose le choix entre différents paradigmes de programmation. Et la période P4 (avril–juin) est consacrée aux réseaux. Sur chacune de ces périodes, les élèves ont environ 40h d'informatique (sauf en P3, moitié moins), c'est-à-dire environ 4h½ par semaine pendant les 9 semaines que dure la période. (Pendant le même temps, les élèves ont bien sûr aussi des cours dans les autres domaines : maths, physique+électronique, sciences économiques et sociales, langues et humanités ; plus des projets et stages variés, notamment entre les périodes. Je dis ça pour situer un peu l'enseignement dans son volume global.)
Pour ce qui est de l'enseignement d'informatique de la période P2 de première année, donc, nous avons divisé la population d'élèves en deux sous-populations inégales : ceux qui sont entrés par les filières MPI ou MP option info du concours commun ainsi que les admis sur titres après une licence d'informatique, soit au total ~90 élèves, et tous les autres, qui doivent représenter à peu près ~120 élèves. Les premiers ont eu le cours INF110 dont je me suis chargé et dont je parle ici, tandis que les seconds ont eu un cours (INF109) plus général sur l'algorithmique fondamentale et quelques rudiments de la calculabilité (recouvrant notamment une partie de ce que les autres élèves avaient déjà vu en prépa).
Bref, s'agissant de INF110, j'avais à m'occuper de 39 heures d'enseignement (contrôle de connaissances non compris), étalées sur 9 semaines, pour ~90 élèves. Nous avons décidé de séparer ces 39h en 21h de cours magistraux, et 18h de TD+TP lesquels ont été pris en charge par mon collègue Théo Z. Pour des raisons d'organisation de l'emploi du temps global de la première année, il n'était pas possible de regrouper les élèves tous ensemble pendant les cours magistraux, donc j'ai donné deux fois chaque séance (on arrive donc à 42h pour moi, si vous suivez).
De l'opportunité d'enseigner des sujets théoriques
Il faut que je dise ici un mot pour évoquer la question qui revient
de façon récurrente dès qu'on parle de dispenser des cours assez
« théoriques » (whatever that means) à des élèves
majoritairement[#2] futurs
ingénieurs. J'avais déjà droit à ce genre de remarques quand
j'enseignais l'analyse de Fourier (cf. par
exemple ce billet) : à quoi ça
sert d'expliquer à des futurs ingénieurs la différence entre la
transformée de Fourier dans L¹ et dans L² ? ce n'est pas ça qui va
leur servir : de toute façon leur signal sera un ensemble fini de
valeurs
. Donc inévitablement on peut se demander pourquoi
enseigner la calculabilité ou la logique intuitionniste quand ce même
temps pourrait être employé à faire des cours de JavaScript et de
Python, parce que c'est ça qui va leur servir — ou bien
d'IA puisque maintenant tout le monde se rue sur
l'IA comme des lemmings sur la falaise.
[#2] Je précise
que majoritairement
ne signifie pas exclusivement
. Même
sans parler des cas vraiment exotiques, et il y en a toujours (après
tout, j'ai dans ma promo à Normale Sup un camarade qui a décidé que la
passion de sa vie c'était le cirque, et qui a donc fait carrière dans
les arts du spectacle après une thèse de physique quantique), il y a
quand même une proportion, faible mais non complètement négligeable,
d'élèves à Télécom qui décident de faire de la recherche plus ou moins
théorique, c'est-à-dire de faire une thèse et éventuellement d'en
faire carrière. Néanmoins, j'entends bien l'argument selon lequel
l'enseignement ne doit pas se construire en fonction des intérêts
d'une minorité. Donc ce n'est pas l'argument que j'invoque
ici pour justifier l'intérêt de mon cours.
J'avais déjà répondu à cette objection, mais je ne sais pas si ma réponse me satisfait beaucoup. Disons que si un ingénieur n'est pas un chercheur (dont le métier consiste à proposer de nouvelles théories), il n'est pas non plus un technicien (dont le métier consiste à appliquer des techniques existantes) : l'ingénieur est justement là pour faire le pont entre les deux, pour appliquer la théorie à la création de nouvelles techniques ou au perfectionnement de celles qui existent, ce qui implique de bien maîtriser la théorie de son domaine, avec un certain recul sur celle-ci, pas juste comme des recettes de cuisine apprises par cœur. (Je devrais, ici, faire référence à la nouvelle Profession d'Asimov, mais c'est un peu difficile à faire sans divulgâcher ; donc je vais me contenter de dire : lisez-la.)
On se trompe gravement, je pense, en voulant centrer l'enseignement
sur ce qui sert (ou plutôt, on se trompe gravement en ne
mesurant pas toutes les manières dont quelque chose
peut servir
) : le but de l'enseignement, quel que soit son
niveau, n'est pas principalement, ou en tout cas pas exclusivement, de
fournir des outils qui serviront mais aussi des clés de compréhension
de phénomènes, des cadres dans lesquels analyser ce à quoi on est
confronté. Les outils, ce n'est pas qu'il ne faut pas les apprendre,
mais ils ont tendance à être hautement spécifiques à une tâche
précise, les outils nécessaires à tel ou tel métier s'apprennent
normalement en entrant dans ce métier, ce n'est tout simplement pas le
rôle de l'enseignement de former à s'en servir (sauf dans la mesure où
ces outils sont extrêmement transverses). S'agissant de
l'informatique, donc, on se trompe par exemple en pensant qu'il faut
enseigner les langages de programmation qui seront utilisés ensuite :
un langage de programmation, ça s'apprend rapidement, l'intérêt de les
enseigner n'est pas d'enseigner ce qui servira mais
d'enseigner ce qui permet de comprendre les différentes
approches[#3] de la
programmation et les différentes façon de concevoir une tâche.
[#3] Sur les langages
de programmation, ce que je dis c'est que ça n'a, selon moi, aucun
intérêt de se dire je vais apprendre/enseigner JavaScript et Python
parce que c'est ce qui me/leur servira après
; il faut plutôt se
dire je vais viser un échantillon représentatif des différents
styles de langages de programmation pour comprendre les différentes
approches : un langage bas niveau pour comprendre comment fonctionne
la gestion de la mémoire, un langage purement fonctionnel et paresseux
pour comprendre comment conceptualiser les tâches par une
programmation d'ordre supérieur, un langage orienté objet pour
comprendre le concept d'héritage, un langage distribué pour comprendre
les difficultés de la programmation asynchrone et la manière dont on
les résout, etc.
Donc l'intérêt d'enseigner la calculabilité, ce n'est certainement pas parce que ça va servir (même s'il est certainement utile qu'un ingénieur sache que certaines tâches ne sont pas réalisables algorithmiquement — et qu'en gros toute tentative d'analyser le comportement d'un programme devra reposer sur des heuristiques qui seront fatalement imparfaites), mais aussi parce que cela amène à prendre du recul sur les concepts fondamentaux de l'informatique (la récursion, l'auto-référence, la nature de l'évaluation dans les langages de programmation, la raison pour laquelle on se retrouve si facilement à être Turing-complet par accident — et peut-être aussi le fait que ce n'est pas très pertinent de chercher à l'éviter). À titre d'exemple, s'agissant de l'auto-référence, je dirais que quelqu'un qui a du recul sur la calculabilité et le théorème de récursion de Kleene ne sera absolument pas surpris par le fameux hack de Ken Thompson[#4] (d'ailleurs, Thompson lui-même explique le lien entre les quines et son hack), et c'est pertinent pour pouvoir prétendre maîtriser la sécurité informatique. Je peux dire quelque chose d'analogue pour le typage : ce n'est pas juste que c'est utile de comprendre les bases du typage et du polymorphisme parce que certains langages réellement utilisés en ont, mais c'est aussi nécessaire pour comprendre ce qui se passe (par exemple quand on cache des valeur dans une clôture, quand on passe des valeurs par continuation — ce qui sont des paradigmes de programmation qui servent vraiment).
[#4] Dans Reflections
on Trusting Trust, Thompson explique qu'il avait introduit une
backdoor dans le programme login
, puis modifié le
compilateur C pour introduire lui-même cette backdoor si elle ne s'y
trouvait pas, puis pour introduire cette backdoor dans le compilateur
C lui-même, si bien qu'au final il pouvait recompiler le
compilateur avec les sources d'origine et la backdoor
persistait dans le binaire. Voilà quelque chose qui, au minimum, ne
doit pas surprendre un ingénieur en sécurité informatique.