storiq logo

La traînée de poudre

Le blog StorIQ

 

Jeudi, 30 septembre 2010

L’essor de “moins bien, c’est mieux”, par Richard Gabriel (1987).

D’après The rise of Worse is Better.

À peu près tous les concepteurs de Common LISP et CLOS, dont moi-même, ont été extrêmement exposés au style de conception du MIT/Stanford. L’essence de ce style peut être rendue par la phrase : “ce qu’il faut”. Pour un tel concepteur, il est important de réussir à obtenir ces caractéristiques:

  • simplicité - la conception doit être simple, en implémentation comme en interface. Il est plus important pour l’interface d’être simple que pour l’implémentation.
  • exactitude. La conception doit être correcte dans tous les aspects observables. L’inexactitude n’est simplement pas autorisée.
  • Cohérence. Le modèle ne doit pas être incohérent. La conception peut être légèrement moins simple et moins complète pour éviter les incohérences. La cohérence est aussi importante que l’exactitude.
  • Complétude. La conception doit gérer autant de situations importantes que possible. Tous les cas raisonnablement envisageables doivent être couverts. La simplicité n’est pas autorisée aux dépens de la complétude.

Je pense que la plupart des gens admettront que ce sont de bonnes caractéristiques. J’appellerai l’usage de cette philosophie dans la conception “l’approche MIT”. Common LISP (y compris CLOS) et Scheme représentent l’approche MIT dans la conception et l’implémentation.

La philosophie “moins bien, c’est mieux” diffère subtilement:

  • simplicité. la conception doit être simple, en implémentation comme en interface. Il est plus important pour l’implémentation d’être simple que pour l’interface. La simplicité est la plus importante considération dans un concept.
  • Exactitude. La conception doit être correcte dans tous les aspects observables. Il est plus important d’être simple que correct.
  • Cohérence. La conception ne doit pas être incorrecte de manière trop évidente. La cohérence peut être sacrifiée à la simplicité dans certains cas, mais il est préférable d’abandonner les parties qui concernent les circonstances moins courantes que d’introduire soit plus de complexité dans l’implémentation, soit des incohérences.
  • Complétude. La conception doit gérer autant de situations importantes que possible. Tous les cas raisonnablement envisageables devraient être couverts. La complétude peut être sacrifié au profit de toute autre qualité. En fait, la complétude doit être sacrifiée quand la simplicité d’implémentation est en jeu. La cohérence peut être sacrifié pour atteindre la complétude si cela permet de maintenir la simplicité; la cohérence de l’interface n’a pas de valeur particulière.

Les anciennes versions d’Unix et de C sont des exemples de cette école de conception, et j’appellerai cette stratégie “l’approche New Jersey”. J’ai intentionnellement caricaturé la philosophie du “moins bien, c’est mieux” pour vous convaincre que c’est de toute évidence une mauvaise philosophie et que l’approche New jersey est une mauvaise approche.

Cependant, je crois que le “moins bien, c’est mieux”, même présenté comme un épouvantail, a de meilleures capacités de survie que “ce qu’il faut”, et que l’approche New Jersey appliquée au logiciel est supérieure à l’approche MIT.

Permettez moi de répéter une histoire qui montre la validité de la distinction entre MIT et New Jersey, et que les promoteur de chacune de ces philosophies pensent réellement que leur philosophie est la meilleure.

Deux fameux personnages, l’un du MIT et l’autre de Berkeley (mais travaillant sur Unix) se rencontraient pour discuter de problèmes de systèmes d’exploitation. La personne du MIT connaissait bien ITS (le système d’exploitation du labo d’intelligence artificielle du MIT) et avait lu les sources d’Unix. Il voulait savoir comment Unix résolvait le problème du contrôle de processus “loser”. Ce problème apparaît lorsqu’un programme utilisateur invoque une routine système pour effectuer une opération de longue durée qui peut avoir un état significatif, comme des tampons d’entrée-sortie. Si une interruption se produit pendant cette opération, l’état du programme utilisateur doit être sauvegardé. Comme l’invocation de la routine système est généralement une seule instruction, le contrôle de processus du programme ne peut capturer de manière adéquate l’état du processus. La routine système doit donc soit ressortir, soit continuer sur sa lancée. “Ce qu’il faut” est ressortir, restaurer le programme utilisateur à l’instruction précise qui a invoqué la routine système afin qu’à la reprise du programme utilisateur après l’interruption, par exemple, on rentre à nouveau dans la routine système. On appelle cela contrôle de processus “loser” parce que le contrôle de processus est forcé en mode “loser”, “loser” étant le petit nom pour “utilisateur” au MIT.

Le gars du MIT ne voyait aucun code pour prendre soin de ce cas et demanda au gars du New Jersey comment le problème était pris en charge. Le gars du New Jersey répondit que les gens d’Unix étaient conscients du problème, mais que la solution était pour la routine système de toujours finir, mais parfois un code d’erreur serait retourné pour signaler que la routine avait échoué à terminer son action. Un programme utilisateur correct, donc, devait vérifier le code d’erreur pour déterminer s’il devait simplement essayer d’appeler la routine système à nouveau. Le gars du MIT n’aimait pas cette solution parce que ce n’était pas “ce qu’il faut”.

Le gars du New jersey dit que la solution Unix était correcte, parce que la philosophie de conception d’Unix était la simplicité et que “ce qu’il faut” serait trop complexe. Par ailleurs, les programmeurs pouvaient facilement ajouter ce test et cette boucle supplémentaires. Le gars du MIT remarque que l’implémentation était simple mais que l’interface de la fonctionnalité était complexe. Le gars du New Jersey fit alors que le bon compromis avait été choisi dans Unix, à savoir la simplicité d’implémentation était plus importante que la simplicité de l’interface.

Le gars du MIT marmonna alors que parfois il faut être un vrai dur pour faire un poulet bien tendre, mais le gars du New Jersey n’a pas compris (je ne suis pas sûr d’avoir compris non plus).

Maintenant je veux arguer le fait que “moins bien c’est mieux” est mieux. C est un langage de programmation conçu pour écrire Unix, et il a été conçu en suivant l’approche New Jersey. C est par conséquent un langage pour lequel il est facile d’écrire un compilateur acceptable, et il impose au programmeur d’écrire du texte facile à interpréter par le compilateur. Certains ont dit que le C était un langage assembleur sophistiqué. À la fois les anciens Unix et compilateurs C avaient des structures simples, étaient facile à porter, demandaient peu de ressources machine pour s’exécuter, et fournissaient à peu près 50 à 80 % de ce que l’on attend d’un système d’exploitation et d’un langage de programmation.

La moitié des ordinateurs existant à un instant quelconque sont inférieurs à la moyenne (plus petits, plus lents). Unix et C fonctionnent bien sur ceux-ci. L’approche “moins bien, c’est mieux” signifie que la simplicité d’implémentation a la plus haute priorité, ce qui veut dire qu’Unix et C sont faciles à porter sur de telles machines. Par conséquent, puisque les 50% de fonctionnalité fournies par C et Unix sont satisfaisants, on s’attend à ce qu’ils commencent à apparaître partout. Et c’est bien ce qu’il s’est passé, n’est ce pas?

Unix et C sont les virus informatiques ultimes.

Un bénéfice supplémentaire de l’approche “moins bien, c’est mieux” est que le programmeur est conditionné à sacrifier de la sûreté, de la commodité et des ennuis pour obtenir une bonne performance et une utilisation modeste de ressources. Les programmes écrits en utilisant l’approche New Jersey fonctionneront bien sur de petites machines comme sur des grosses, et le code sera portable puisqu’il est écrit par dessus un virus.

Il est important de se rappeler que le virus initial doit être globalement bon. Dans ce cas, la propagation virale est assurée aussi longtemps qu’il reste portable. Une fois que le virus s’est répandu, on fera pression pour l’améliorer, peut-être en augmentant la fonctionnalité plus près de 90%, mais les utilisateurs ont déjà été habitué à accepter quelque chose d’inférieur à “ce qu’il faut”. En conséquence, le logiciel “moins bien c’est mieux” premièrement commencera à être accepté, deuxièmement habituera les utilisateurs à en attendre moins, et troisièmement sera amélioré jusqu’au point où il sera presque comme il faut. En terme concrets, bien que les compilateurs Lisp en 1987 aient été à peu près aussi bons que les compilateurs C, il y a nettement plus d’experts en compilateurs qui veulent améliorer les compilateurs C que les compilateurs Lisp.

La bonne nouvelle c’est qu’en 1995 nous auront un bon système d’exploitation et un bon langage de programmation; la mauvaise nouvelle c’est que ce seront Unix et C++.

Il y a un dernier avantage à “moins bien c’est mieux”: Comme le système et le langage du New Jersey ne sont pas vraiment assez puissants pour construire de grands et complexes logiciels monolithiques, les grands systèmes doivent être conçu pour réutiliser des composants. Par conséquent une tradition d’intégration fleurit.

Comment “ce qu’il faut” se défend-il? Il y a deux scénarios de base : le scénario du “grand système complexe” et celui du “pur joyau”.

Le scénario du “grand système complexe” se déroule ainsi:

Tout d’abord, le système “qu’il faut” doit être conçu. Ensuite l’implémentation doit en être conçue. Enfin il est implémenté. Comme c’est “ce qu’il faut”, il a pratiquement 100% de la fonctionnalité souhaitée, et la simplicité d’implémentation n’ayant pas été une préoccupation cela a pris très longtemps. Le système est vaste et complexe. Il nécessite des outils complexes pour l’utiliser correctement. Les derniers 20% prennent 80% de l’effort, et de fait “ce qu’il faut” prend du temps, et ne fonctionne correctement que sur le matériel le plus sophistiqué.

Le scénario du “pur joyau” se déroule ainsi:

La conception de “ce qu’il faut” demande une éternité, mais le système est plutôt compact. L’implémenter de façon à ce qu’il soit rapide est soit impossible, soit au-delà des capacités de la plupart des développeurs.

Ces deux scénarios correspondent à Common Lisp et Scheme.

Le premier scénario est aussi celui des logiciels d’intelligence artificielle classique.

“Ce qu’il faut” est fréquemment un logiciel monolithique, mais sans raison autre que sa conception est menée ainsi. De fait cette caractéristique est accidentelle.

La leçon à retenir de tout ceci est qu’il est souvent indésirable de commencer par “ce qu’il faut”. Il est préférable d’avoir la moitié de “ce qu’il faut” disponible afin qu’il se répande comme un virus. Une fois que les gens s’y sont attachés, prenez le temps de l’améliorer jusqu’à 90% de “ce qu’il faut”.

La leçon à ne surtout pas retenir serait de prendre cette parabole littéralement et d’en conclure que C est le bon véhicule pour l’intelligence artificielle. La solution à 50% doit être à peu près juste, et dans ce cas elle ne l’est pas.

Mais on peut aussi conclure que la communauté Lisp doit sérieusement reconsidérer sa position sur la conception en Lisp. J’en dirai plus sur ce sujet ultérieurement.

rpg@lucid.com

posté à: 14:47 permalink