À dire : Bonjour à toutes et à tous. Le quiz est corrigé ; aujourd'hui, nous changeons de rythme. Plutôt qu'un atelier commun, chacun de vous va choisir un projet, le démarrer ici avec moi, puis le terminer à la maison. Je vous présente d'abord les sujets et la façon de choisir, puis la manière de démarrer et ce que vous rendrez. Ensuite, la séance vous appartient presque entièrement. Commençons par ce que recouvre ce projet. Repères : avant cette page, le quiz sur AP8, dix minutes de réponse puis cinq de correction ensemble, pris sur la marge du créneau (quiz/quiz-9-replication, chapitre 11 du poly). Le lancement commun dure environ 20 min, de cette page à la première consigne. Version préparatoire : les sujets marqués « prototype » dans le catalogue restent à calibrer avant janvier.
À dire : Ce projet est noté, et c'est vous qui choisissez son sujet, parmi onze regroupés en quatre thèmes ; chaque thème prolonge une partie du cours. Vous n'avez pas à cumuler les sujets ni leurs variantes : un seul projet, mené aussi loin que possible et bien expliqué. Nous le démarrons ensemble aujourd'hui, et vous le poursuivez à la maison. Il comprend trois éléments : votre code, des tests que vous aurez écrits vous-mêmes, et un compte rendu qui explique vos choix et vos limites. Les modalités, c'est-à-dire le barème, la charge attendue et l'échéance, sont en bas de la page. Voyons maintenant les sujets, thème par thème. Repères : 2 min. La dernière ligne affiche « à préciser » tant que les modalités ne sont pas arrêtées ; la compléter avant la séance, sans improviser un barème à l'oral. « Au choix » porte sur le sujet ; le projet choisi est noté. Aucun ancien rendu de fin de séance ni bonus de finition ne s'applique.
À dire : Premier thème, les langages et les machines, dans le prolongement d'AP2 et AP3. Le sujet Types reprend la question laissée ouverte en AP3 : que peut-on savoir d'un programme avant de l'exécuter ? Vous écrivez un typeur, des règles sur les nombres jusqu'au polymorphisme, guidé par quatre carnets. Le backend CHIP-8 traduit nos arbres en instructions pour une petite console existante : vous compilez des calculs, des branches et un dessin, puis, au palier avancé, des appels et la récursion terminale d'AP1 pour faire tourner un jeu de Sokoban. Dans les deux cas, le parser et l'outillage sont fournis : votre travail porte sur l'algorithme. Passons à l'apprentissage. Repères : 2 min. Aucun lexer ni parser terminé d'AP2 n'est requis. Les carnets Types guident un seul projet Mix : ne pas demander deux implémentations. CHIP-8 calcule sur huit bits, avec des AST écrits à la main comme en AP3 ; l'émulateur Elixir reste une ressource de réserve, hors catalogue.
À dire : Deuxième thème, l'apprentissage, dans le prolongement d'AP4 et AP5. L'autoencodeur comprime une petite image en quelques nombres, puis la reconstruit, même abîmée ; le VAE en est un prolongement. Micrograd vous fait écrire ce que grad faisait pour nous en AP5 : le moteur qui calcule les dérivées en remontant un graphe de calcul. L'image par coordonnées apprend la couleur d'un pixel à partir de sa position, puis compare ce que change un encodage avec des sinus. Le transformer, enfin, apprend à recopier une séquence grâce à un calcul appelé attention. Les données et l'entraînement sont fournis et tournent sur vos machines : vous écrivez les fonctions qui font comprendre le mécanisme, puis une expérience expliquée. Troisième thème : la concurrence. Repères : 2 min. Aucune donnée externe, aucun GPU ni compte de service. Ne pas promettre du français au transformer, ni une généralisation à partir des quatre points XOR ou des pixels d'une seule image. Autoencodeur, micrograd et image par coordonnées sont des prototypes.
À dire : Troisième thème, la concurrence, dans le prolongement d'AP6 à AP8. Dans les deux premiers sujets, vous écrivez vous-mêmes la coordination des processus. La ferme de rendu confie des morceaux d'image à des workers : il faut borner les calculs en cours, reconstruire l'image quand les résultats arrivent dans le désordre et survivre à la panne d'un worker. Les réservations reprennent le GenServer d'AP7 : deux clients veulent la même place, et une seule transition doit décider à qui elle revient. Les deux éditeurs, eux, fournissent les processus et l'interface : vous écrivez l'algorithme qui fait converger deux copies, avec des identités de caractères pour le CRDT, guidé, ou en transformant des positions pour l'OT, plus exigeant. Dernier thème, l'algorithmique. Repères : 2 min. Ferme de rendu et réservations sont des prototypes. Une accélération n'est pas garantie, et la convergence d'un texte n'est pas une attribution exclusive. Inutile de construire les deux éditeurs. Aucun HTML, CSS ou JavaScript n'est à écrire.
À dire : Dernier thème, l'algorithmique, avec un seul sujet : répartir des élèves fictifs dans des groupes selon leurs vœux. Vous écrirez d'abord une acceptation différée, où les admissions restent provisoires, et qui produit une affectation stable. Vous construirez ensuite un réseau de flot pour savoir si tout le monde peut obtenir l'un de ses premiers choix. Ces objectifs ne coïncident pas, et c'est tout l'intérêt : comprendre ce que chaque algorithme garantit. Comment choisir parmi ces onze sujets ? Repères : 2 min. Prototype. Données fictives : aucune donnée de la promotion, aucune contrainte administrative du script réel. Le coût minimal est un prolongement guidé, et la stabilité n'est pas son objectif.
À dire : Choisissez une question dont vous savez déjà décrire un petit exemple : c'est le meilleur signe que le sujet vous parlera. Avant de vous décider, faites tourner le premier essai fourni et lisez le contrat de la première fonction à écrire ; ces quelques minutes vous évitent un mauvais choix. Méfiez-vous du nombre de fonctions : l'éditeur CRDT n'en compte que trois, mais ses garanties se justifient. Enfin, un socle bien compris vaut mieux qu'une accumulation de prolongements. Une fois votre sujet choisi, voici comment nous allons démarrer. Repères : 2 min. Le catalogue reprend les sujets par thème, avec un tableau « Vous souhaitez… / point de départ conseillé ». Si le choix a été demandé avant la séance, raccourcir les quatre pages de thèmes.
À dire : Ces sujets sont plus exigeants qu'un TP de séance, et c'est voulu : nous les démarrons ensemble. Vous partez d'un projet qui fonctionne, dont le premier essai tourne dès le départ. Pendant le premier atelier, je regroupe les élèves d'un même thème pour expliquer la première brique au moment où le besoin apparaît. Ensuite, le README prend le relais : il donne les contrats, les étapes, les erreurs normales et les commandes de vérification. Les interfaces, les tracés et les données sont fournis : vous écrivez l'algorithme. Voyons comment ce travail est organisé. Repères : 2 min. Le démarrage accompagné autorise des sujets plus exigeants qu'un TP découvert seul. Aucun HTML, CSS ou JavaScript n'est demandé. La séance installe le projet ; elle ne promet pas d'en terminer le socle.
À dire : Chaque README découpe le travail en paliers, bronze, argent et or, ou simplement numérotés. Ils ordonnent les idées, chacune s'appuyant sur la précédente ; ce n'est pas un barème. À chaque palier correspondent des tests fournis, que vous activez au fur et à mesure. Vous y ajoutez vos propres tests, en particulier un contre-exemple qui ferait échouer une version presque juste. Enfin, le compte rendu explique comment lancer votre projet, ce qui fonctionne, les choix que vous avez faits et les limites qui restent. Pour tout cela, quelques commandes Mix suffisent. Repères : 2 min. Le poids du compte rendu dans la note et sa longueur ne sont pas fixés. Les tests personnels ont un modèle dans chaque projet, par ex. test/personnel_test.exs pour les Types. Le nombre de fonctions, de tests ou de paliers ne mesure ni le temps nécessaire ni la note.
À dire : Vous connaissez Mix depuis AP6. Certains projets demandent d'abord de télécharger leurs dépendances. Le premier test à lancer est celui du code fourni : selon le sujet, son étiquette s'appelle provided ou fourni, et il doit passer. Lancez ensuite mix test tout court : vos étapes échouent avec « à compléter », c'est normal, cela vous montre le travail. Chaque palier suivant s'active en ajoutant son étiquette, et les commandes se cumulent. Pour essayer une fonction à la main, iex -S mix charge le projet. Vous êtes prêts à démarrer. Repères : 1 min. Les étiquettes changent selon le sujet, argent et or pour les Types, palier_2 pour le CRDT ou le backend : renvoyer au README. Une erreur d'installation se traite séparément d'un échec « à compléter ». Avec Elixir 1.20, des avertissements de types sur les corps encore réduits à raise sont attendus.
À dire : À vous. Téléchargez le squelette de votre sujet depuis la page AP9 et dézippez-le directement dans votre dépôt ap-homework : surtout pas de git init dans ce dossier, sinon votre travail ne m'arrivera jamais. Lancez le premier essai du README en prédisant d'abord ce qu'il doit afficher. Lisez ensuite le contrat de la première fonction et commencez à l'écrire. Notez dans le compte rendu une prédiction et le premier souci que vous rencontrez. Je passe voir chaque thème dans quelques minutes. Repères : 2 min avec la mise en route, puis 50 min de premier atelier accompagné. Point de passage : un petit cas prédit et une première brique écrite. Faire identifier le fichier à modifier et le prochain test avant que chacun poursuive. Créer COMPTE-RENDU.md s'il n'existe pas dans le squelette.
À dire : Pendant ce premier atelier, je rassemble quelques minutes les élèves d'un même thème, au moment où ils abordent la première brique. Pour les Types et l'éditeur CRDT, je m'appuie sur les pages qui suivent ; pour les autres sujets, sur la fiche du site, son schéma et le README. Si votre groupe n'est pas concerné, poursuivez votre essai : ces pages ne sont pas un exposé pour tous. Commençons par celles et ceux qui ont choisi les Types. Repères : pages de référence pour les groupes concernés, à ne pas dérouler devant toute la classe ; compter environ cinq minutes par thème, à la demande. Les schémas des autres sujets sont sur leurs fiches.
À dire : Voici les briques de votre typeur, à lire de gauche à droite. Un visiteur parcourt l'arbre du programme et produit des contraintes, c'est-à-dire des égalités entre types. L'unification les résout et les rassemble dans une substitution. Au moment d'un let, la généralisation fabrique un schéma, que chaque lecture du nom instancie avec des inconnues neuves. Ce sont les mêmes arbres qu'en AP2, avec vos propres modules. Voyons ces contraintes sur une petite fonction. Repères : le diaporama AP9-typeur déroule chacune de ces briques ; les quatre carnets Types donnent les liens précis vers ses pages, sans recopier tout le cours. Aucun lexer ni parser terminé n'est requis : le squelette les fournit.
À dire : Prenons cette petite fonction. Nous ne savons rien de x : donnons-lui un type inconnu, a. L'addition exige deux nombres flottants, donc a doit valoir Float, et son résultat est Float. La fonction a donc le type fn(Float) -> Float. Chaque opération ajoute ainsi une égalité qu'il faut rendre vraie. Une fois le palier argent écrit, votre MiniTypeur.check doit retrouver exactement ce résultat. Un let va pourtant nous demander davantage. Repères : faire reconstruire les trois étapes oralement. Dans le squelette, MiniTypeur.check("fn(x) { x + 1.0 }") rend {:ok, "fn(Float) -> Float"} une fois le palier argent écrit ; au bronze, utiliser Regles.infer/1. Si les étapes ne sont pas claires, reprendre les carnets Types 2 puis 3 avant de choisir une extension.
À dire : Ici, id est utilisée deux fois, avec un nombre puis avec un booléen. Si id gardait une seule inconnue, le premier appel la fixerait à Float et le second échouerait. Le let conserve donc un schéma, « pour tout a, fn(a) -> a », et chaque usage reçoit ses propres inconnues fraîches. Ce n'est pas vrai pour un paramètre : dans le corps d'une fonction, un paramètre f appliqué à 1.0 puis à True est refusé, car il garde la même inconnue jusqu'au bout. Savoir dire ce qui est partagé et ce qui est renouvelé, c'est le cœur du palier or. Avant d'aller plus loin, consolidons. Repères : polymorphisme, un même nom utilisable à plusieurs types. Le contre-exemple du paramètre f vient du carnet Types 4. Il faut savoir expliquer ce qui est partagé avant d'ajouter une nouvelle forme de type.
À dire : Avant de prolonger, vérifiez le parcours principal : les règles, les fonctions, puis le polymorphisme. Lisez le carnet de votre prochaine brique, puis écrivez-la dans le projet Mix, qui reste votre seule version de référence. Gardez un exemple accepté, un exemple refusé et une trace de contraintes expliquée : ce sont les pièces de votre compte rendu. Relancez vos tests en cumulant les paliers. Les trois pages suivantes présentent les extensions possibles, une fois ce socle compris. Repères : les commandes sont cumulatives, mix test, puis --include argent, puis --include argent --include or ; --include or seul laisse argent exclu. Le point de passage est annoncé en séance. Un élève qui découvre les types peut passer la séance sur les carnets 1 et 2 : c'est un vrai travail, pas un retard.
À dire : Première extension, un type composé : la paire, qui garde deux types éventuellement différents. Ajouter une forme d'arbre et une forme de type oblige à revoir toutes les fonctions qui les parcourent. Lesquelles, selon vous ? … Il faut que resolve, free, unify et visit sachent traverser une paire. Une inconnue peut désormais se cacher dans un composant : l'occurs-check et la généralisation doivent la voir. Une fois la paire en place, nous pouvons en extraire un élément. Repères : attendre resolve, free, unify et visit ; format seulement si l'affichage est choisi. Le parser fourni ne reconnaît pas les paires : commencer par des AST écrits à la main.
À dire : La projection first extrait le premier élément d'une paire. Dans cette fonction, nous ne connaissons pas le type de p. La projection impose qu'il soit une paire de deux types inconnus, a et b, et le résultat a le type a. Le piège serait de ne reconnaître qu'une paire écrite en toutes lettres : first doit aussi fonctionner sur un nom ou sur un appel. C'est donc une contrainte de type, pas une forme syntaxique. Troisième extension, indépendante des deux autres : dire où le typage échoue. Repères : extension après les paires. Créer deux inconnues fraîches, unifier le type de l'argument avec {:pair, a, b}, rendre a. Une implémentation qui ne reconnaîtrait que le nœud {:pair, ...} littéral échouerait sur un nom ou un appel.
À dire : Notre typeur refuse cette expression, mais sans dire où. Le désaccord vient de l'addition intérieure, qui reçoit un booléen : c'est le fils droit de l'addition extérieure. Sans positions dans le texte source, un chemin dans l'arbre suffit à le désigner, ici [:right]. Gardez la raison du refus et ajoutez-lui son emplacement. Passons maintenant à celles et ceux qui ont choisi l'éditeur CRDT. Repères : extension indépendante des paires. Le carnet fixe la convention : le chemin part de la racine, [] désigne le nœud courant et [:left] son fils gauche ; un désaccord porte le chemin du nœud qui impose la contrainte, un nom absent celui du nœud name.
À dire : Alice et Bob partent d'une même copie de ab. Sans échanger, chacun insère une lettre à l'indice 1 : Alice obtient aXb, Bob aYb. Quand ils se retrouvent, comment réunir leurs écritures, dans le même ordre chez tous les deux ? L'indice 1 ne désigne plus le même endroit chez l'un et chez l'autre : c'est tout le souci. Un CRDT est une structure dont les copies se fusionnent dans n'importe quel ordre et finissent par coïncider, comme le G-Counter d'AP8. Voyons comment il représente un texte. Repères : les processus, le parcours de l'arbre et toute l'interface web sont fournis ; aucun HTML, CSS ou JavaScript n'est demandé. La variante OT, plus exigeante, est un autre sujet du catalogue.
À dire : Regardons le panneau de gauche. Chaque caractère reçoit un identifiant, fait d'un compteur et du nom de son auteur, et retient son ancre : le caractère visible qui le précédait à l'insertion. Le texte se lit en affichant chaque parent avant ses enfants, et les frères par identifiant décroissant : {3, :bob} passe avant {3, :alice}, d'où aYXb chez tous les deux. À droite, Alice supprime a pendant que Bob insère Z juste après a. Supprimer ne fait que masquer l'entrée : Z garde son ancre et reste visible après la fusion. Voyons maintenant ce que vous écrivez. Repères : le document ab doit être créé une seule fois puis copié, sinon quatre lettres apparaissent. Le nouvel identifiant de Z est {4, :bob}. Les atomes départagent les compteurs égaux.
À dire : Dans ce projet, vous écrivez trois fonctions pures du module Document : insérer une entrée, supprimer en marquant son identifiant, et fusionner deux états. Le parcours de l'arbre, les GenServers et toute l'interface sont déjà écrits. Lancez l'application et ouvrez la page : les deux éditeurs s'affichent, et une modification vous signale la fonction qui manque. Au départ, mix test échoue sur les six tests du premier palier, et c'est attendu. Commencez par lire ces tests et prévoir leurs résultats. Une question vous accompagnera ensuite tout le projet : converger, est-ce suffisant ? Repères : le parcours guidé comprend insert/4, delete/2 et merge/2, des scénarios personnels transformés en tests et un compte rendu sur les lois et leurs limites. Les helpers gèrent les indices, le caractère et le prochain identifiant. La fusion porte sur des snapshots valides et compatibles, avec un seul écrivain par identité. Si le port 4009 est occupé, le README donne la variante avec PORT.
À dire : Votre éditeur garantit une chose précise : deux copies qui ont reçu les mêmes informations affichent le même texte, dans le même ordre. Il ne garantit pas que cet ordre respecte l'intention de chacun ; l'article cité montre des cas où l'ordre obtenu mélange des passages écrits en même temps. Les snapshots restent en mémoire, les suppressions s'accumulent, et les curseurs, l'annulation ou la reprise après panne sont d'autres questions. Ce sont exactement les limites que votre compte rendu doit savoir nommer. Repères : discussion à reprendre au fil du projet selon l'avancement. Adaptation pédagogique inspirée de RGA, pas un éditeur de production. Les horloges sont logiques : elles ordonnent les identifiants sans mesurer l'ordre réel des frappes. Tant qu'un snapshot n'est pas arrivé, deux copies différentes ne sont pas un bug.
À dire : Arrêtons-nous cinq minutes avant la pause. J'aimerais entendre deux ou trois d'entre vous, de thèmes différents, sur un essai de ce premier atelier. Qu'aviez-vous prédit, et qu'avez-vous observé ? Si quelque chose vous a arrêté, quel est ce souci, et quelle piste suivez-vous ? Les autres, écoutez : un souci rencontré dans un autre sujet ressemble souvent au vôtre. Après la pause, nous repartons exactement de là. Repères : 5 min, puis 10 min de pause. Partir des essais des élèves, sans distribuer de corrigé. Une observation surprenante nourrit la reprise même si le programme reste incomplet.
À dire : Reprenons. Commencez par relancer vos tests : votre première brique passe-t-elle vraiment, y compris sur un cas que vous n'aviez pas prévu ? Écrivez ensuite un test à vous, qui ferait échouer une version presque juste : c'est souvent là que l'on comprend le mieux son sujet. Puis avancez d'une étape dans le README, en gardant la trace de ce que vous avez prédit et observé. Je continue à passer par thème, et nous ferons le point ensemble en fin de séance. Repères : 75 min. Point de passage : la brique vérifiée, un cas personnel et la suite engagée. Une erreur d'installation se traite à part, en binôme si besoin.
À dire : Pour finir, comparons nos démarches, thème par thème. Montrez un résultat que vous aviez prédit, et dites quelle règle ou quel calcul le justifie. Quel contre-exemple vous a fait comprendre votre représentation ? Quelle information traverse votre algorithme, et quelle limite reste dans votre version ? Pour l'apprentissage, je vous demanderai ce qui change entre deux essais et sur quelles données porte la mesure ; pour un algorithme, un invariant et un cas limite ; pour les processus, qui possède l'état et quel message peut arriver en retard. Terminons par une question commune à tous les sujets. Repères : 15 min, trois retours courts de cinq minutes à partir des essais des élèves. Aucun format de nouvelle évaluation n'est fixé ici. L'oral sert à vérifier la compréhension et à choisir la prochaine étape, sans fournir le corrigé.
À dire : Tous ces sujets se ramènent à une même question : quelle information faut-il conserver pour retrouver le résultat ? Dans le typeur, un type inconnu relie plusieurs contraintes ; dans l'éditeur, un identifiant relie plusieurs éditions ; dans un réseau, les axes d'un tenseur séparent les exemples de leurs composantes ; dans une affectation, les capacités résiduelles permettent de revenir sur un choix. Dans votre compte rendu, expliquez pourquoi l'information que vous conservez est nécessaire. Avant de partir, poussez votre dépôt et notez votre prochaine étape à la maison. Bon travail à toutes et à tous ! Repères : bilan individuel d'environ 5 min. Dans les sujets de machine, faire nommer aussi la valeur que les registres ou la pile doivent préserver ; dans les sujets de processus, le jeton ou l'identifiant qui relie une réponse à sa demande. Ces exemples relient les thèmes sans imposer de traiter les autres sujets.