Pas plus tard qu’hier, quelqu’un m’a posé la question : mais quel est donc le programme de l’Ensimag, côté info, en première année ? C’est vrai qu’à lire les sources officielles vagues et pompeuses (dont je ne donnerai pas l’URL tellement elle est imbittable ; par contre vous pouvez aller jeter un œil à la page d'EnsiZéro concernant le programme, qui n’est ma foi pas mal faite), on n’a guère idée de ce qui nous attend, et je me rappelle m’être demandé également, avant d’intégrer. Du coup, entre un récap xmltools et un futur article-touchette signé bluestorm, j’ai décidé de mettre à jour ma catégorie Ensimag — en sommeil depuis plus d’un mois — avec un article de choc : l’Ensimag pour les véritables.
Imaginez : vous allez entrer à l’Ensimag, et vous êtes un véritable — bon, disons un apprenti-véritable, ou même juste un troll puant, en fait, mais disons que vous avez une idée de ce que peut être l’informatique ou la programmation, ptet pas la bonne idée, mais une idée. Il y a certainement des choses que vous aimeriez savoir, pour mieux vous préparer (psychologiquement) à ce qui vous guette. Vous n’avez besoin de savoir que ce qui compte, ouais, stuff that matters (parce que vous êtes in, parlez le franglais, et lisez /.), des choses comme le nom du système d’hérétiques que l’on vous forcera à utiliser, ou du langage de pouilleux que l’on vous imposera sans merci et qui sans nul doute fera ressentir à votre esprit d’athlète toute la désespératitude de devoir s’exprimer dans ce corps infirme (mais qu’il saura, je suis sûr, évacuer, selon le cas, dans l’alcool, la drogue, la déprim ou l’aigrissement).
Tout d’abord, rassurez-vous, on ne vous enseignera probablement rien qui pourrait venir profondément bouleverser votre conception de la vie et de l’univers. Il faudra cependant accepter que tout le monde ne partage pas (ou plutôt que pas grand monde ne partage) votre spécialité, vos intérêts ou vos convictions, non seulement parmi vos Petits Camarades mais également vos profs, ces gentilles dames et ces gentils messieurs qui auront à leur charge de vous apprendre de gentilles choses.
Une fois ceci bien digéré, la vérité est… que vous allez faire de l’Ada sur des thin clients relié à un serveur Linux (Red Hat) quasiment toute l’année, avec trois semaines de C pour clore la danse en guise de projet de fin. Entre temps, vous vous serez arraché les cheveux sur Scilab (c’est statistique), aurez découvert (ou redécouvert) le VHDL (si vous choisissez Archi2 au second semestre), vaguement touché de l’ASM x86 (Logiciel de base), et aperçu de loin du Prolog (si vous prenez Logique au second semestre).
Vous n’aurez ni programmation objet (mais pas de réjouissances hâtives, on en mange en 2A), ni programmation fonctionnelle (bouh). On vous épargnera même tout le B^Hplaisir de faire de la modélisation avec UML, Merise et leurs amis.
Le reste des matières est constitué de maths, de culture générale informatique (dans le genre de « comment se servir d’un shell » ou « SSH et ses copains crypto-gentils »), et de sciences sociales et humaines (et malgré le nom, ce n’est pas incompatible avec la nolife-attitude).
Voilà donc un tour d’horizon de ce qui sommeille de ce côté-ci de la planète Ensimag ; au-delà, il vous faudra trouver d’autres sources que mon humble blog.
# bluestorm
14.08.09, 19:12.
# rz0
14.08.09, 22:12.
Tu sembles oublier que j’ai commencé la programmation en Turbo Pascal. :) Mais sinon bah, hum, mon expérience de l’Ada n’est pas trop mauvaise, mais loin d’être bonne.
Le principal problème que j’ai vu c’est qu’il n’y a pas de pointeurs comme en C, seulement une version très pauvre, et aucun mécanisme assez puissant et pratique pour compenser. Bien sûr, ce ne serait pas un problème si l’impératif n’était pas le paradigme dominant, mais il l’est. Après, c’est sûrement mes habitudes de programmeur C qui ressortent.
L’exemple que j’ai sous la main, là, c’est celui du parcours d’une structure simplement chaînée, dans un seul sens, où tu veux pouvoir rechaîner le nœud sur lequel tu t’arrêtes (c’est typiquement impératif et bien sûr, la solution fonctionnelle est une alternative, mais Ada se veut un langage impératif ou alors je n’ai rien compris). En C, tu peux laisser traîner un pointeur sur le pointeur membre de la structure qui te permet d’avancer, c’est sûrement la méthode la plus élégante.
En Ada, tu ne peux pas, car les pointeurs ne sont pas aliasables par défaut, et les rendre éligibles demande de modifier toutes les déclarations, parce que le type n’est pas le même (ce qui est normal). On peut voir ces contraintes comme un système similaire à celui introduit par
restricten C99, mais avec une philosophie complètement inversée : par défaut, rien n’est aliasable et il faut explicitement déclarer les choses pour qu’elles le soient (en vérité, les tableaux en Ada étant dissociés des pointeurs, le problème adressé n’est pas le même, mais c’est une bonne analogie). Cela paraît intéressant : pour mettre en rapport ce qui n’a rien avoir, c’est un peu le principe du moindre privilège. :) Mais en pratique, c’est foireux, parce que si en C tu peux abandonner ton droit à l’aliasing temporairement dans un contexte local, l’inverse n’est bien entendu pas possible, donc utiliser des pointeurs sur objets possiblement confondus est une grande décision en Ada et relève de la conception et non de l’implémentation. Tu ne peux pas dire, « cet algo-ci, je l’implémente avec des pointeurs aliasés parce que c’est cool ». Cela rend leur usage marginal.La solution en Ada dans le cas simple est de tout récrire en récursif terminal et d’utiliser des paramètres
in out:C’est meugnon comme tout, faut avouer, mais ça manque de flexibilité, et dans des cas plus complexes, ça pêche. Typiquement, cela se gâte quand on veut faire du réutilisable, car le design récursif se marie bien avec les fonctions d’ordre supérieur. En C, je peux transformer mon précédent algo en une fonction qui renvoie deux pointeurs : un vers l’objet lui-même et un vers le pointeur qui le référence. En Ada, en voulant imiter, le mieux que je pourrais faire est de renvoyer un pointeur vers le nœud précédent. Mais là, on se heurte à un problème d’homogénéité : les racines n’ont pas de prédécesseur. Du coup, on se retrouve à devoir traiter à part des cas marginaux et cela se répercute vite à travers tout le code, ce qui duplique le code utile et généralement rend les choses dégueux.
Alors bien sûr, on se dit que le fonctionnel c’est cool, que l’on devrait prendre exemple et passer des fonctions. Qu’à cela ne tienne, il y a des génériques en Ada ! En plus, on ne se tape pas les perfs douteuses des appels indirects, w00t. Mais la syntaxe est tellement lourde que cela n’en vaut guère la peine, et enchaîner les génériques devient vite un calvaire. Ma règle du doigt est que quand la quantité de code nécessaire à la déclaration des génériques et de duplication du typage qu’il engendre approche dangereusement de celle de code utile, il faut faire sans. J’ai déjà réussi à écrire du code générique avec les routines communes factorisées qui prenait plus de place que son équivalent avec duplication du code utile mais sans génériques…
Le second point qui me chagrine beaucoup en Ada, du moins avec GNAT, qui, il faut bien se le dire, semble être la seule implémentation viable en existence, est que les exceptions, très très présentes un peu partout dans le langage, sont horriblement lentes. Je me rappelle de mon premier TP où les profs annonçaient une heure pour faire tourner le programme, et où, un jour avant le rendu, je me suis aperçu qu’en remplaçant les exceptions par des retours de fonction comme en C, je passais de trois heures à huit minutes. Fais le calcul par toi-même, cela fait presque 22 fois plus rapide !
Bien sûr, quand j’ai dit ça aux profs, ils ont tenté de relativiser la chose, de trouver des excuses à la pauvre bête, mais il ne faut pas se voiler la face, on aurait dit ça de Ruby, ça aurait fait rigoler tout le monde.
# bluestorm
15.08.09, 07:42.
Pour la liste, je ne suis pas sûr que ce que tu décris soit spécifiquement impératif : n’est-ce pas la même chose avec le
List.tlde caml, qui renvoie un pointeur vers la queue de la liste, et crée donc un alias si on a gardé la liste initiale accessible ?Si c’est bien ce dont tu parles, je pense que la solution est effectivement de rendre les pointeurs d’une linked-list aliasables, parce que le fait de "pointer un noeud interne comme sous-liste de ses successeurs" est une opération naturelle, quel que soit le paradigme.
Pour les génériques, effectivement quand j’ai vu le design du truc je me suis un peu méfié. Ça fait un peu polymorphisme pour les pauvres, et si tu les utilises sur de grosses fonctions peu timing-critiques tu perds rapidement en taille du code les avantages que tu gagnes en performances. Après il y a aussi des compilos SML qui spécialisent comme ça, donc ça doit avoir son intérêt dans certains cas, mais au moins au niveau de la syntaxe il vaut mieux du vrai polymorphisme. C’était sans doute trop expérimental au moment du design d’ADA, mais maintenant le polymorphisme à la ML c’est bien connu, et surtout si on fait pas d’inférence ça ajoute pas beaucoup de complexité.
C’est vrai aussi que la lourdeur de la syntaxe d’une feature joue beaucoup sur son intérêt dans la pratique. C’est assez malheureux parce que ça joue souvent en défaveur de la compréhensibilité en poussant les designers à choisir les syntaxes les plus concises mais pas forcément les plus explicites. Une solution pourrait être de régulièrement prévoir deux syntaxes, une courte pour les petits bouts de code, et une longue pour les gros. Ça fait un peu usine à gaz, mais en pratique pas mal de languages ont fait ça pour certaines features seulement, de manière assez ad hoc, et au final un peu d’uniformité pourrait être bénéfique (en C if/else et l’opérateur conditionnel ternaire, en C# les méthodes anonymes et les lambda-expressions, en Caml begin/end et les parenthèses, etc.).
Et tu as vu un peu les features de modularité du langage ? Il me semble que dans le monde impératif c’est un plus récents relativement mainstream à s’occuper vraiment de la modularité sans obligatoirement faire de POO. Ça change du C ?
:-`NB : Si si pour le Pascal, c’était une référence — pas assez explicite on dirait.
# rz0
15.08.09, 09:26.
En fait je me rends compte qu’en l’état, ce que j’ai dit est faux. :D Les pointeurs en Ada en fait s’aliasent, dans le sens où tu peux avoir deux pointeurs sur la même chose, mais ne s’alisent pas avec n’importe quoi, plutôt.
J’ai un peu été vite en besogne et je me suis trop branlé sur le parallèle avec
restrictdu coup j’ai un peu oublié la réalité et le cas « évident » que tu mentionnes parce que hum, c’était… trop évident. Mais en fait non, d’après ma formulation, donc je vais détailler.En fait, un Ada, les objets dynamiques (dynamiquement alloués) sont aliasables mais les autres non, par défaut. En plus de ça, un pointeur par défaut ne peut contenir que l’adresse d’un objet dynamique. Bref, ça demande deux déclarations pour avoir un pointeur comme en C :
Genre, si t’as un type :
Bah, tu peux aliaser des
Acces_Listeentre eux pourvu qu’ils soient dynamiquement alloués, mais par contre, si dans une fonction tu déclares :Bah un pointeur de type
Acces_Listene peut pas pointer surL. Faudrait rajouteraliasedà la déclaration d’Let rajouterallà celle d’Acces_Liste.Puis, imagine tu balances maintenant :
Bah ça en gros, t’en fais rien, parce que ça ne peut aliaser que les pointeurs de pointeurs dynamiquement alloués, soit rien d’utile. Et même si tu rajoutes
all, bah ça ne pourra toujours pas pointer sur le champSuivde la structureListeparce que faudrait y rajouteraliased. Tak tak.Et c’est de ce dernier point dont je parlais, pouvoir pointer sur le champ
Suivavec un pointeur de pointeur. En Ada tu peux oublier.Donc pour me corriger parce que dire de la merde sur mon propre blog c’est pas cool : les pointeurs en Ada ne s’aliasent pas par défaut avec des objets statiques.
À part ça, question modularité, les packages, c’est sympa, mais c’est un peu lourd à l’usage. Les génériques sur des packages, c’est acceptable parce que tu dilues la déclaration lourde dans plus de fonctions importées au final. Les règles de portée sont sympas, mais on est au final loin de la flexibilité qu’offre les templates du C++ (ou alors faudrait bourrer des tonnes de code dupliqué, ce qui est horrible) ou des foncteurs Caml (de ce que j’en connais).
On reproche au C le fait de devoir dupliquer les signatures des fonctions en déclaration/définition, bah l’Ada reprend ça (et à mon avis, en pire, avec les génériques en plus, du coup tu en arrives à déclarer trois ou quatre fois la même signature), alors qu’honnêtement, ya pas de raison (en fait si, on pourrait avoir besoin du fichier d’interface avec une lib pré-compilée mais chai pas, ils avaient qu’à dire que les interfaces dans ce cas doivent être pré-compilées aussi, bref) ; le C utilise des
#includedonc les déclarations ne sont pas couplées avec le code d’implémentation, mais en Ada, tout est couplé alors pourquoi diable doit-on tout déclarer en double ?Par rapport au C, le gros plus niveau modularité, c’est les génériques, mais comme je le disais, la syntaxe est trop lourde ; j’aime bien le concept mais il faudrait moins avoir à déclarer le moindre ptit truc. En C++, tout le monde râle que c’est crade que les paramètres en entrée de templates bah ça peut être n’importe quoi, donc tu peux avoir un
class Adont après tu appelles des méthodes un peu n’importe comment. Mais en Ada, il faut tout déclarer, et je trouve que ça devient vite très relou. En gros, si ton packageAaccepte un paramètre génériqueTqui lui-même est un package, et que disons tu veux queTexposefetgcomme fonctions ; bah en Ada, tu déclares un package génériqueBqui prend deux paramètres génériquesfetgavec les bonnes signatures, puis tu dis àAd’accepterTune instance deB, et au moment d’instancierA, tu le fais avec une instance deBà laquelle t’auras refourguer les fonctions qui vont bien (tu pourrais directement spécifierfetgcomme paramètres génériques d’A, mais je fais exprès de prendre un design factorisé pour que tu vois comment ça fait lourd quand tu veux faire « propre »). Maintenant, là où ça fait mal au cul, c’est que tu ne peux pas dire « mon packageCimplémente l’interface offerte parBdonc hop op pop je fileCàA». Non, faut que dans C tu recopies toutes les signatures demandées parB, que tu les implémentes bien sûr, puis que tu crées ton instance deBet que tu lui donnes les fonctions qui vont bien (ça tu peux le faire comme un sous-package deCpour que ce soit réutilisable, à la rigueur), et que tu refiles ça àA. Ça ne paraît pas choquant comme ça mais si tu asC,DetEqui implémentent tousB(ce qui est le but avec un tel design) bah je te laisse imaginer da pain. Du coup ça s’auto-mutile quoi ; ça a les moyens de faire des choses propres, mais comme tu te retrouves comme un con à recopier à droite et à gauche ton typage ultra-verbeux bah tu fais rien de ta vie ; t’as pondu des centaines de lignes de code avec du vide.CimplémenteB» mais j’en doute fortement étant donné que j’ai lu des gens dire qu’ils utilisaient la méthode manno (mais ça ya bien des gens qui font du C avec les genoux), mais surtout que je n’ai rien vu dans la norme qui semble indiquer que c’est possible. Mais si ya un guru Ada qui passe par là, je veux bien sa version des faits, ça m’évitera de m’auto-dégoûter la prochaine fois que j’ai envie de faire des choses trop folles en Ada. :D# bluestorm
15.08.09, 10:51.
Ça je pense que c’est parce qu’Ada n’a pas de sous-typage au niveau des packages, tu dois écrire les projections toi-même au lieu de le laisser le faire (éventuellement en le lui demandant explicitement). Les modules ML ont ça; ils ont même mieux, un sous-typage structurel, tu peux dire "toute signature qui implémente f et g avec ces types là" et t’as pas besoin de passer par une interface B intermédiaire, ni de préciser dans les déclarations de C, D et E qu’elles satisfont bien B comme on le fait en sous-typage nominal (qui a aussi ses avantages) comme dans la plupart des langages OO. Mais la théorie derrière est tout de suite plus compliquée et ça existait pas trop au début d’ADA (même s’ils auraient pu le rajouter après, s’ils ont mis de la POO ils doivent bien avoir du sous-typage).
PS : j’ai l’impression que les commentaires ont un peu dévié du billet initial. C’est gênant ? Je suis prêt à essayer la prochaine fois de trouver le point de déviation et envoyer plutôt un mail à ce moment là. Sinon, une pratique courante chez certains bloggueurs c’est, quand ils veulent répondre de manière circonstanciée et intéressante, de faire un billet dédié à ça en citant le ou les commentaires instigateurs. Tu devrais l’envisager, ça te ferait plus de billets à peu de frais, et sans compromis non plus sur la qualité (même si omg il a dit un truc faux, il doit en être tout traumatisé).
# Bastien S
18.08.09, 14:58.
# Mtzgrmstr
20.08.09, 11:02.