LA REVANCHE DES INTELLOS
OriginalMai 2002
"Nous étions à la recherche des programmeurs C++. Nous avons réussi à en entraîner un bon nombre à mi-chemin vers Lisp."
- Guy Steele, co-auteur de la spécification Java
Dans l'industrie du logiciel, il y a une lutte constante entre les universitaires à tête pointue et une autre force tout aussi redoutable, les patrons à tête pointue. Tout le monde sait qui est le patron à tête pointue, n'est-ce pas ? Je pense que la plupart des gens dans le monde de la technologie non seulement reconnaissent ce personnage de bande dessinée, mais connaissent également la personne réelle dans leur entreprise sur laquelle il est modelé.
Le patron à tête pointue combine miraculeusement deux qualités qui sont courantes individuellement, mais rarement vues ensemble : (a) il ne sait absolument rien de la technologie, et (b) il a des opinions très arrêtées à ce sujet.
Supposons, par exemple, que vous deviez écrire un logiciel. Le patron à tête pointue n'a aucune idée du fonctionnement de ce logiciel, et ne peut pas distinguer un langage de programmation d'un autre, et pourtant il sait dans quel langage vous devriez l'écrire. Exactement. Il pense que vous devriez l'écrire en Java.
Pourquoi pense-t-il cela ? Examinons l'esprit du patron à tête pointue. Ce qu'il pense, c'est quelque chose comme ça. Java est une norme. Je sais que ça doit l'être, car j'en ai lu parler dans la presse tout le temps. Puisque c'est une norme, je ne risquerai pas d'avoir des problèmes en l'utilisant. Et cela signifie aussi qu'il y aura toujours beaucoup de programmeurs Java, donc si les programmeurs qui travaillent pour moi maintenant démissionnent, comme les programmeurs qui travaillent pour moi le font mystérieusement toujours, je pourrai facilement les remplacer.
Eh bien, cela ne semble pas si déraisonnable. Mais tout cela repose sur une hypothèse inavouée, et cette hypothèse s'avère être fausse. Le patron à tête pointue croit que tous les langages de programmation sont à peu près équivalents. Si c'était vrai, il aurait raison. Si les langages sont tous équivalents, bien sûr, utilisez celui que tout le monde utilise.
Mais tous les langages ne sont pas équivalents, et je pense pouvoir vous le prouver sans même entrer dans les différences entre eux. Si vous aviez demandé au patron à tête pointue en 1992 dans quel langage les logiciels devraient être écrits, il aurait répondu avec autant d'assurance qu'aujourd'hui. Les logiciels devraient être écrits en C++. Mais si les langages sont tous équivalents, pourquoi l'opinion du patron à tête pointue devrait-elle changer ? En fait, pourquoi les développeurs de Java se seraient-ils même donné la peine de créer un nouveau langage ?
Vraisemblablement, si vous créez un nouveau langage, c'est parce que vous pensez qu'il est meilleur d'une certaine manière que ce que les gens avaient déjà. Et en fait, Gosling indique clairement dans le premier livre blanc sur Java que Java a été conçu pour résoudre certains problèmes avec C++. Donc voilà : les langages ne sont pas tous équivalents. Si vous suivez le fil de la pensée du patron à tête pointue jusqu'à Java, puis en remontant l'histoire de Java jusqu'à ses origines, vous finissez par tenir une idée qui contredit l'hypothèse de départ.
Alors, qui a raison ? James Gosling, ou le patron à tête pointue ? Pas étonnant que Gosling ait raison. Certains langages sont meilleurs, pour certains problèmes, que d'autres. Et vous savez, cela soulève des questions intéressantes. Java a été conçu pour être meilleur, pour certains problèmes, que C++. Quels problèmes ? Quand Java est-il meilleur et quand C++ l'est-il ? Y a-t-il des situations où d'autres langages sont meilleurs que l'un ou l'autre ?
Une fois que vous commencez à considérer cette question, vous ouvrez une véritable boîte de Pandore. Si le patron à tête pointue devait réfléchir au problème dans toute sa complexité, cela ferait exploser son cerveau. Tant qu'il considère tous les langages comme équivalents, tout ce qu'il a à faire est de choisir celui qui semble avoir le plus de dynamisme, et comme c'est plus une question de mode que de technologie, même lui peut probablement trouver la bonne réponse. Mais si les langages varient, il doit soudain résoudre deux équations simultanées, essayant de trouver un équilibre optimal entre deux choses qu'il ne connaît pas : la pertinence relative des vingt ou trente principaux langages pour le problème qu'il doit résoudre, et les chances de trouver des programmeurs, des bibliothèques, etc. pour chacun. Si c'est ce qui se cache derrière la porte, il n'est pas surprenant que le patron à tête pointue ne veuille pas l'ouvrir.
L'inconvénient de croire que tous les langages de programmation sont équivalents est que ce n'est pas vrai. Mais l'avantage est que cela simplifie beaucoup votre vie. Et je pense que c'est la principale raison pour laquelle cette idée est si répandue. C'est une idée confortable.
Nous savons que Java doit être assez bon, car c'est le nouveau langage de programmation tendance. Ou est-ce le cas ? Si vous regardez le monde des langages de programmation de loin, il semble que Java soit la dernière nouveauté. (De loin, tout ce que vous pouvez voir est le grand panneau d'affichage clignotant payé par Sun.) Mais si vous regardez ce monde de plus près, vous découvrez qu'il y a des degrés de tendance. Au sein de la sous-culture des hackers, il y a un autre langage appelé Perl qui est considéré comme beaucoup plus tendance que Java. Slashdot, par exemple, est généré par Perl. Je ne pense pas que vous trouveriez ces gars-là utilisant Java Server Pages. Mais il y a un autre langage plus récent, appelé Python, dont les utilisateurs ont tendance à regarder de haut Perl, et plus en attente.
Si vous regardez ces langages dans l'ordre, Java, Perl, Python, vous remarquez un modèle intéressant. Du moins, vous remarquez ce modèle si vous êtes un hacker Lisp. Chacun d'entre eux ressemble de plus en plus à Lisp. Python copie même des fonctionnalités que de nombreux hackers Lisp considèrent comme des erreurs. Vous pourriez traduire de simples programmes Lisp en Python ligne par ligne. Nous sommes en 2002, et les langages de programmation ont presque rattrapé 1958.
Rattraper les mathématiques
Ce que je veux dire, c'est que Lisp a été découvert pour la première fois par John McCarthy en 1958, et les langages de programmation populaires ne font que rattraper maintenant les idées qu'il a développées à cette époque.
Maintenant, comment cela peut-il être vrai ? L'informatique n'est-elle pas quelque chose qui évolue très rapidement ? Je veux dire, en 1958, les ordinateurs étaient des monstres de la taille d'un réfrigérateur avec la puissance de calcul d'une montre-bracelet. Comment une technologie aussi ancienne pourrait-elle être pertinente, voire supérieure aux derniers développements ?
Je vais vous dire comment. C'est parce que Lisp n'a pas vraiment été conçu pour être un langage de programmation, du moins pas dans le sens où nous l'entendons aujourd'hui. Ce que nous entendons par un langage de programmation, c'est quelque chose que nous utilisons pour dire à un ordinateur ce qu'il doit faire. McCarthy avait finalement l'intention de développer un langage de programmation dans ce sens, mais le Lisp que nous avons finalement obtenu était basé sur quelque chose de séparé qu'il a fait comme exercice théorique - un effort pour définir une alternative plus pratique à la machine de Turing. Comme l'a dit McCarthy plus tard,
Une autre façon de montrer que Lisp était plus élégant que les machines de Turing était d'écrire une fonction Lisp universelle et de montrer qu'elle était plus brève et plus compréhensible que la description d'une machine de Turing universelle. C'était la fonction Lisp eval, qui calcule la valeur d'une expression Lisp.... Écrire eval nécessitait d'inventer une notation représentant les fonctions Lisp comme des données Lisp, et une telle notation a été conçue aux fins de l'article sans que l'on pense qu'elle serait utilisée pour exprimer des programmes Lisp dans la pratique.
Ce qui s'est passé ensuite, c'est que, quelque part à la fin de 1958, Steve Russell, l'un des étudiants diplômés de McCarthy, a regardé cette définition de eval et a réalisé que s'il la traduisait en langage machine, le résultat serait un interpréteur Lisp.
C'était une grande surprise à l'époque. Voici ce que McCarthy a dit à ce sujet plus tard dans une interview :
Steve Russell a dit, regarde, pourquoi je ne programme pas cet eval..., et je lui ai dit, ho, ho, tu confonds théorie et pratique, cet eval est destiné à la lecture, pas au calcul. Mais il a quand même fait ça. C'est-à-dire qu'il a compilé l'eval de mon article en code machine [IBM] 704, en corrigeant les bugs, et l'a ensuite annoncé comme un interpréteur Lisp, ce qu'il était certainement. Donc à ce moment-là, Lisp avait essentiellement la forme qu'il a aujourd'hui....
Soudainement, en quelques semaines je pense, McCarthy a trouvé que son exercice théorique s'était transformé en un véritable langage de programmation - et plus puissant que ce qu'il avait prévu.
Donc l'explication succincte de la raison pour laquelle ce langage des années 1950 n'est pas obsolète est que ce n'était pas la technologie mais les mathématiques, et les mathématiques ne deviennent pas désuètes. La bonne chose à comparer à Lisp n'est pas le matériel des années 1950, mais disons l'algorithme de tri rapide, qui a été découvert en 1960 et est toujours le tri à usage général le plus rapide.
Il y a un autre langage qui survit encore des années 1950, Fortran, et il représente l'approche opposée à la conception du langage. Lisp était un morceau de théorie qui a été transformé de manière inattendue en un langage de programmation. Fortran a été développé intentionnellement comme un langage de programmation, mais ce que nous considérerions maintenant comme très bas niveau.
Fortran I, le langage qui a été développé en 1956, était un animal très différent du Fortran d'aujourd'hui. Fortran I était à peu près du langage d'assemblage avec des mathématiques. À certains égards, il était moins puissant que les langages d'assemblage plus récents ; il n'y avait pas de sous-routines, par exemple, seulement des branches. Le Fortran d'aujourd'hui est maintenant probablement plus proche de Lisp que de Fortran I.
Lisp et Fortran étaient les troncs de deux arbres évolutifs distincts, l'un enraciné dans les mathématiques et l'autre dans l'architecture des machines. Ces deux arbres ont convergé depuis lors. Lisp a commencé par être puissant et, au cours des vingt années suivantes, est devenu rapide. Les soi-disant langages grand public ont commencé par être rapides et, au cours des quarante années suivantes, sont devenus progressivement plus puissants, jusqu'à ce que maintenant les plus avancés d'entre eux soient assez proches de Lisp. Proches, mais il leur manque encore quelques choses...
Ce qui a rendu Lisp différent
Lorsqu'il a été développé pour la première fois, Lisp incarnait neuf nouvelles idées. Certaines d'entre elles sont maintenant des acquis, d'autres ne se voient que dans les langages les plus avancés, et deux sont encore uniques à Lisp. Les neuf idées sont, dans l'ordre de leur adoption par le courant dominant,
Les conditionnelles. Une conditionnelle est une construction de type if-then-else. Nous les tenons pour acquises maintenant, mais Fortran I ne les avait pas. Il n'avait qu'un goto conditionnel étroitement basé sur l'instruction machine sous-jacente.
Un type de fonction. Dans Lisp, les fonctions sont un type de données comme les entiers ou les chaînes de caractères. Elles ont une représentation littérale, peuvent être stockées dans des variables, peuvent être passées en arguments, et ainsi de suite.
La récursivité. Lisp a été le premier langage de programmation à la prendre en charge.
Le typage dynamique. Dans Lisp, toutes les variables sont en fait des pointeurs. Les valeurs ont des types, pas les variables, et l'affectation ou la liaison de variables signifie copier des pointeurs, pas ce qu'ils pointent.
La collecte des ordures.
Des programmes composés d'expressions. Les programmes Lisp sont des arbres d'expressions, chacune renvoyant une valeur. C'est à l'opposé de Fortran et de la plupart des langages qui ont suivi, qui distinguent les expressions et les instructions.
Il était naturel d'avoir cette distinction dans Fortran I car on ne pouvait pas imbriquer les instructions. Et donc, bien que vous ayez besoin d'expressions pour que les mathématiques fonctionnent, il n'y avait aucun intérêt à faire que quoi que ce soit d'autre renvoie une valeur, car il n'y avait rien qui l'attendait.
Cette limitation a disparu avec l'arrivée des langages à structure de blocs, mais à ce moment-là, c'était trop tard. La distinction entre expressions et instructions était ancrée. Elle s'est répandue de Fortran à Algol, puis à leurs descendants.
Un type de symbole. Les symboles sont en fait des pointeurs vers des chaînes de caractères stockées dans une table de hachage. Donc vous pouvez tester l'égalité en comparant un pointeur, au lieu de comparer chaque caractère.
Une notation pour le code utilisant des arbres de symboles et de constantes.
Tout le langage là tout le temps. Il n'y a pas de réelle distinction entre le temps de lecture, le temps de compilation et le temps d'exécution. Vous pouvez compiler ou exécuter du code pendant la lecture, lire ou exécuter du code pendant la compilation, et lire ou compiler du code à l'exécution.
Exécuter du code au moment de la lecture permet aux utilisateurs de reprogrammer la syntaxe de Lisp ; l'exécuter au moment de la compilation est la base des macros ; compiler à l'exécution est la base de l'utilisation de Lisp comme langage d'extension dans des programmes comme Emacs ; et lire à l'exécution permet aux programmes de communiquer à l'aide d'expressions s, une idée récemment réinventée sous le nom d'XML.
Lorsque le Lisp est apparu pour la première fois, ces idées étaient très éloignées de la pratique de programmation ordinaire, qui était largement dictée par le matériel disponible à la fin des années 1950. Au fil du temps, le langage par défaut, incarné dans une succession de langages populaires, a progressivement évolué vers le Lisp. Les idées 1-5 sont maintenant répandues. Le numéro 6 commence à apparaître dans le courant dominant.
Python a une forme de 7, bien qu'il ne semble pas y avoir de syntaxe pour cela.
En ce qui concerne le numéro 8, c'est peut-être le plus intéressant du lot. Les idées 8 et 9 ne sont devenues partie intégrante du Lisp que par accident, parce que Steve Russell a implémenté quelque chose que McCarthy n'avait jamais eu l'intention d'implémenter. Et pourtant, ces idées se révèlent être responsables de l'apparence étrange du Lisp et de ses caractéristiques les plus distinctives. Le Lisp semble étrange non pas tant à cause de sa syntaxe étrange que parce qu'il n'a pas de syntaxe ; vous exprimez les programmes directement dans les arbres d'analyse qui sont construits en coulisse lorsque d'autres langages sont analysés, et ces arbres sont composés de listes, qui sont des structures de données Lisp.
Exprimer le langage dans ses propres structures de données s'avère être une fonctionnalité très puissante. Les idées 8 et 9 ensemble signifient que vous pouvez écrire des programmes qui écrivent des programmes. Cela peut sembler une idée bizarre, mais c'est une chose courante en Lisp. La façon la plus courante de le faire est avec quelque chose appelé une macro.
Le terme "macro" ne signifie pas en Lisp ce qu'il signifie dans d'autres langages. Une macro Lisp peut être tout, d'une abréviation à un compilateur pour un nouveau langage. Si vous voulez vraiment comprendre le Lisp, ou simplement élargir vos horizons de programmation, en apprendre davantage sur les macros.
Les macros (au sens du Lisp) sont encore, à ma connaissance, uniques au Lisp. C'est en partie parce que pour avoir des macros, vous devez probablement rendre votre langage aussi étrange que le Lisp. C'est peut-être aussi parce que si vous ajoutez cette dernière touche de puissance, vous ne pouvez plus prétendre avoir inventé un nouveau langage, mais seulement un nouveau dialecte du Lisp.
Je mentionne cela surtout comme une blague, mais c'est tout à fait vrai. Si vous définissez un langage qui a car, cdr, cons, quote, cond, atom, eq et une notation pour les fonctions exprimées sous forme de listes, alors vous pouvez construire tout le reste du Lisp à partir de là. C'est en fait la qualité définissante du Lisp : c'est pour faire en sorte que cela soit possible que McCarthy a donné au Lisp la forme qu'il a.
Où les langages comptent
Donc, supposons que le Lisp représente une sorte de limite que les langages dominants approchent de manière asymptotique - cela signifie-t-il que vous devriez vraiment l'utiliser pour écrire des logiciels ? À quel point perdez-vous en utilisant un langage moins puissant ? N'est-il pas plus sage, parfois, de ne pas être à la pointe de l'innovation ? Et la popularité n'est-elle pas, dans une certaine mesure, une justification en soi ? Le patron à tête pointue n'a-t-il pas raison, par exemple, de vouloir utiliser un langage pour lequel il peut facilement embaucher des programmeurs ?
Il y a, bien sûr, des projets où le choix du langage de programmation n'a pas beaucoup d'importance. En règle générale, plus l'application est exigeante, plus vous tirez parti de l'utilisation d'un langage puissant. Mais de nombreux projets ne sont pas du tout exigeants. La plupart de la programmation consiste probablement à écrire de petits programmes de liaison, et pour ces petits programmes de liaison, vous pouvez utiliser n'importe quel langage avec lequel vous êtes déjà familiarisé et qui a de bonnes bibliothèques pour ce dont vous avez besoin. Si vous avez juste besoin de transférer des données d'une application Windows à une autre, utilisez Visual Basic.
Vous pouvez également écrire de petits programmes de liaison en Lisp (je l'utilise comme calculatrice de bureau), mais le plus gros avantage des langages comme le Lisp se situe à l'autre extrémité du spectre, là où vous devez écrire des programmes sophistiqués pour résoudre des problèmes difficiles face à une concurrence féroce. Un bon exemple est le programme de recherche de tarifs aériens qu'ITA Software concède à Orbitz. Ces gars-là sont entrés sur un marché déjà dominé par deux gros concurrents bien établis, Travelocity et Expedia, et semblent les avoir tout simplement humiliés technologiquement.
Le cœur de l'application d'ITA est un programme Common Lisp de 200 000 lignes qui explore des ordres de grandeur plus de possibilités que leurs concurrents, qui utilisent apparemment encore des techniques de programmation de l'ère des ordinateurs centraux. (Bien qu'ITA utilise aussi, dans un certain sens, un langage de programmation de l'ère des ordinateurs centraux.) Je n'ai jamais vu le code d'ITA, mais selon l'un de leurs meilleurs hackers, ils utilisent beaucoup de macros, et je ne suis pas surpris de l'entendre.
Forces centripètes
Je ne dis pas qu'il n'y a pas de coût à l'utilisation de technologies peu courantes. Le patron à tête pointue n'a pas complètement tort de s'en inquiéter. Mais parce qu'il ne comprend pas les risques, il a tendance à les amplifier.
Je peux penser à trois problèmes qui pourraient découler de l'utilisation de langages moins courants. Vos programmes pourraient ne pas bien fonctionner avec des programmes écrits dans d'autres langages. Vous pourriez avoir moins de bibliothèques à votre disposition. Et vous pourriez avoir du mal à embaucher des programmeurs.
Quelle est l'importance de chacun de ces problèmes ? L'importance du premier varie selon que vous ayez ou non le contrôle de l'ensemble du système. Si vous écrivez un logiciel qui doit s'exécuter sur la machine d'un utilisateur distant sur un système d'exploitation bogué et fermé (je ne cite pas de noms), il peut y avoir des avantages à écrire votre application dans le même langage que le système d'exploitation. Mais si vous contrôlez l'ensemble du système et avez le code source de toutes les parties, comme le fait probablement ITA, vous pouvez utiliser les langages que vous voulez. Si un problème d'incompatibilité survient, vous pouvez le corriger vous-même.
Dans les applications serveur, vous pouvez vous permettre d'utiliser les technologies les plus avancées, et je pense que c'est la principale cause de ce que Jonathan Erickson appelle la "renaissance des langages de programmation". C'est pourquoi on entend même parler de nouveaux langages comme Perl et Python. On n'entend pas parler de ces langages parce que les gens les utilisent pour écrire des applications Windows, mais parce que les gens les utilisent sur des serveurs. Et à mesure que les logiciels se déplacent hors du bureau et sur les serveurs (un avenir auquel même Microsoft semble résigné), il y aura de moins en moins de pression pour utiliser des technologies moyennes.
Quant aux bibliothèques, leur importance dépend également de l'application. Pour les problèmes les moins exigeants, la disponibilité des bibliothèques peut l'emporter sur la puissance intrinsèque du langage. Où se situe le point d'équilibre ? Difficile à dire exactement, mais quel qu'il soit, il est en deçà de tout ce qu'on pourrait appeler une application. Si une entreprise se considère comme étant dans le secteur des logiciels et qu'elle écrit une application qui sera l'un de ses produits, il s'agira probablement d'un projet impliquant plusieurs hackers et prenant au moins six mois à écrire. Dans un projet de cette taille, les langages puissants commencent probablement à l'emporter sur la commodité des bibliothèques préexistantes.
La troisième inquiétude du patron à tête pointue, la difficulté d'embaucher des programmeurs, je pense que c'est un faux problème. Combien de hackers avez-vous besoin d'embaucher, après tout ? Sûrement que nous savons tous maintenant que les logiciels sont mieux développés par des équipes de moins de dix personnes. Et vous ne devriez pas avoir de mal à embaucher des hackers à cette échelle pour n'importe quel langage dont quelqu'un a déjà entendu parler. Si vous ne pouvez pas trouver dix hackers Lisp, alors votre entreprise est probablement basée dans la mauvaise ville pour développer des logiciels.
En fait, choisir un langage plus puissant réduit probablement la taille de l'équipe dont vous avez besoin, car (a) si vous utilisez un langage plus puissant, vous n'aurez probablement pas besoin d'autant de hackers, et (b) les hackers qui travaillent dans des langages plus avancés sont probablement plus intelligents.
Je ne dis pas que vous n'allez pas subir beaucoup de pression pour utiliser ce qui est perçu comme des technologies "standard". Chez Viaweb (maintenant Yahoo Store), nous avons soulevé quelques sourcils parmi les investisseurs et les acquéreurs potentiels en utilisant Lisp. Mais nous avons aussi soulevé des sourcils en utilisant des boîtiers Intel génériques comme serveurs au lieu de serveurs "à la force industrielle" comme les Suns, en utilisant une variante Unix open source alors peu connue appelée FreeBSD au lieu d'un véritable système d'exploitation commercial comme Windows NT, en ignorant une prétendue norme de commerce électronique appelée SET que personne ne se rappelle maintenant, et ainsi de suite.
Vous ne pouvez pas laisser les costumes prendre des décisions techniques pour vous. Cela a-t-il alarmé certains acquéreurs potentiels que nous utilisions Lisp ? Certains, légèrement, mais si nous n'avions pas utilisé Lisp, nous n'aurions pas été en mesure d'écrire le logiciel qui les a fait vouloir nous acheter. Ce qui leur semblait être une anomalie était en fait la cause et l'effet.
Si vous démarrez une startup, ne concevez pas votre produit pour plaire aux VC ou aux acquéreurs potentiels. Concevez votre produit pour plaire aux utilisateurs. Si vous gagnez les utilisateurs, tout le reste suivra. Et si ce n'est pas le cas, personne ne se souciera du caractère rassurant de vos choix technologiques.
Le coût d'être moyen
Combien perdez-vous à utiliser un langage moins puissant ? Il existe en fait des données sur le sujet.
La mesure de puissance la plus pratique est probablement la taille du code. L'objectif des langages de haut niveau est de vous donner de plus grandes abstractions - de plus gros briques, pour ainsi dire, afin que vous n'ayez pas besoin d'autant pour construire un mur d'une taille donnée. Donc plus le langage est puissant, plus le programme est court (pas seulement en caractères, bien sûr, mais en éléments distincts).
Comment un langage plus puissant vous permet-il d'écrire des programmes plus courts ? Une technique que vous pouvez utiliser, si le langage vous le permet, est quelque chose appelé programmation ascendante. Au lieu d'écrire simplement votre application dans le langage de base, vous construisez au-dessus du langage de base un langage pour écrire des programmes comme le vôtre, puis vous écrivez votre programme dans celui-ci. Le code combiné peut être beaucoup plus court que si vous aviez écrit tout votre programme dans le langage de base - en fait, c'est ainsi que fonctionnent la plupart des algorithmes de compression. Un programme ascendant devrait également être plus facile à modifier, car dans de nombreux cas, la couche de langage n'aura pas à changer.
La taille du code est importante, car le temps nécessaire pour écrire un programme dépend principalement de sa longueur. Si votre programme serait trois fois plus long dans un autre langage, il vous faudra trois fois plus de temps pour l'écrire - et vous ne pouvez pas y remédier en embauchant plus de personnes, car au-delà d'une certaine taille, les nouveaux arrivants sont en fait une perte nette. Fred Brooks a décrit ce phénomène dans son célèbre livre The Mythical Man-Month, et tout ce que j'ai vu a eu tendance à confirmer ce qu'il a dit.
Alors, à quel point vos programmes sont-ils plus courts si vous les écrivez en Lisp ? La plupart des chiffres que j'ai entendus pour Lisp par rapport à C, par exemple, ont été d'environ 7 à 10 fois. Mais un article récent sur ITA dans le magazine New Architect indiquait que "une ligne de Lisp peut remplacer 20 lignes de C", et comme cet article était rempli de citations du président d'ITA, je suppose qu'ils ont obtenu ce chiffre d'ITA. Si c'est le cas, nous pouvons y faire confiance ; le logiciel d'ITA comprend beaucoup de C et de C++ ainsi que de Lisp, ils parlent donc d'expérience.
Je pense que ces multiples ne sont même pas constants. Je pense qu'ils augmentent lorsque vous êtes confronté à des problèmes plus difficiles et également lorsque vous avez des programmeurs plus intelligents. Un vrai bon hacker peut tirer plus parti de meilleurs outils.
À titre de point de données sur la courbe, en tout cas, si vous deviez entrer en concurrence avec ITA et choisir d'écrire votre logiciel en C, ils seraient en mesure de développer un logiciel vingt fois plus rapidement que vous. Si vous passiez un an sur une nouvelle fonctionnalité, ils pourraient la dupliquer en moins de trois semaines. Alors que s'ils passaient seulement trois mois à développer quelque chose de nouveau, il faudrait cinq ans avant que vous ne l'ayez aussi.
Et vous savez quoi ? C'est le meilleur des scénarios. Lorsque vous parlez de ratios de taille de code, vous supposez implicitement que vous pouvez effectivement écrire le programme dans le langage le plus faible. Mais en fait, il y a des limites à ce que les programmeurs peuvent faire. Si vous essayez de résoudre un problème difficile avec un langage trop bas niveau, vous atteignez un point où il y a tout simplement trop de choses à garder à l'esprit à la fois.
Donc quand je dis qu'il faudrait cinq ans à l'hypothétique concurrent d'ITA pour dupliquer quelque chose qu'ITA pourrait écrire en Lisp en trois mois, je veux dire cinq ans si tout se passe bien. En fait, la façon dont les choses fonctionnent dans la plupart des entreprises, tout projet de développement qui prendrait cinq ans ne sera probablement jamais terminé.
Je reconnais que c'est un cas extrême. Les pirates d'ITA semblent être inhabituellement intelligents, et C est un langage de très bas niveau. Mais sur un marché concurrentiel, même un écart de deux ou trois à un suffirait à garantir que vous seriez toujours à la traîne.
Une recette
C'est le genre de possibilité dont le patron à tête pointue ne veut même pas penser. Et donc la plupart d'entre eux ne le font pas. Parce que, vous savez, quand on en arrive là, le patron à tête pointue ne se soucie pas que sa société se fasse battre, tant que personne ne peut prouver que c'est de sa faute. Le plan le plus sûr pour lui personnellement est de rester près du centre du troupeau.
Au sein des grandes organisations, l'expression utilisée pour décrire cette approche est "les meilleures pratiques de l'industrie". Son but est de protéger le patron à tête pointue de la responsabilité : s'il choisit quelque chose qui est "les meilleures pratiques de l'industrie" et que l'entreprise perd, il ne peut pas être blâmé. Il n'a pas choisi, c'est l'industrie qui l'a fait.
Je crois que ce terme a été utilisé à l'origine pour décrire les méthodes comptables et autres. Ce que cela signifie, grosso modo, c'est ne faites rien d'étrange. Et en comptabilité, c'est probablement une bonne idée. Les termes "à la pointe" et "comptabilité" ne sonnent pas bien ensemble. Mais quand vous importez ce critère dans les décisions concernant la technologie, vous commencez à obtenir les mauvaises réponses.
La technologie devrait souvent être à la pointe. Dans les langages de programmation, comme l'a fait remarquer Erann Gat, ce que "les meilleures pratiques de l'industrie" vous donnent en réalité, ce n'est pas le meilleur, mais simplement la moyenne. Lorsqu'une décision vous amène à développer des logiciels à une fraction du rythme de concurrents plus agressifs, "les meilleures pratiques" est un abus de langage.
Donc voici deux informations que je pense être très précieuses. En fait, je le sais par ma propre expérience. Numéro 1, les langages varient en puissance. Numéro 2, la plupart des gestionnaires ignorent délibérément cela. Entre eux, ces deux faits sont littéralement une recette pour gagner de l'argent. ITA est un exemple de cette recette en action. Si vous voulez gagner dans une entreprise de logiciels, il suffit de vous attaquer au problème le plus difficile que vous puissiez trouver, d'utiliser le langage le plus puissant que vous puissiez obtenir et d'attendre que les patrons à tête pointue de vos concurrents reviennent à la moyenne.
Annexe : Puissance
Pour illustrer ce que je veux dire par la puissance relative des langages de programmation, considérons le problème suivant. Nous voulons écrire une fonction qui génère des accumulateurs - une fonction qui prend un nombre n et renvoie une fonction qui prend un autre nombre i et renvoie n incrémenté de i.
(C'est incrémenté de, pas plus. Un accumulateur doit accumuler.)
En Common Lisp, cela donnerait :
(defun foo (n)
(lambda (i) (incf n i)))
et en Perl 5 :
sub foo {
my ($n) = @_;
sub {$n += shift}
}
qui a plus d'éléments que la version Lisp car vous devez extraire manuellement les paramètres en Perl.
En Smalltalk, le code est légèrement plus long qu'en Lisp :
foo: n
|s|
s := n.
^[:i| s := s+i. ]
car bien que les variables lexicales fonctionnent en général, vous ne pouvez pas faire d'affectation à un paramètre, donc vous devez créer une nouvelle variable s.
En JavaScript, l'exemple est à nouveau légèrement plus long, car JavaScript conserve la distinction entre les instructions et les expressions, donc vous avez besoin d'instructions return explicites pour renvoyer des valeurs :
function foo(n) {
return function (i) {
return n += i } }
(Pour être juste, Perl conserve aussi cette distinction, mais le gère à la manière typique de Perl en vous permettant d'omettre les retours.)
Si vous essayez de traduire le code Lisp/Perl/Smalltalk/JavaScript en Python, vous rencontrez certaines limites. Parce que Python ne prend pas complètement en charge les variables lexicales, vous devez créer une structure de données pour contenir la valeur de n. Et bien que Python ait un type de données fonction, il n'y a pas de représentation littérale pour l'un d'eux (sauf si le corps n'est qu'une seule expression), donc vous devez créer une fonction nommée à renvoyer. Voici ce que vous obtenez :
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
Les utilisateurs de Python pourraient légitimement se demander pourquoi ils ne peuvent pas simplement écrire
def foo(n):
return lambda i: return n += i
ou même
def foo(n): lambda i: n += i
et mon hypothèse est qu'ils le feront probablement, un jour.
(Mais s'ils ne veulent pas attendre que Python évolue le reste
du chemin vers Lisp, ils pourraient toujours juste...)
Dans les langages orientés objet, vous pouvez, dans une certaine mesure, simuler
une clôture (une fonction qui fait référence à des variables définies dans
des portées englobantes) en définissant une classe avec une méthode
et un champ pour remplacer chaque variable d'une portée englobante. Cela fait faire à
le programmeur le type d'analyse de code qui serait effectuée par le compilateur dans un langage
avec un support complet de la portée lexicale, et cela ne fonctionnera pas
si plus d'une fonction fait référence à la même variable,
mais c'est suffisant dans des cas simples comme celui-ci.
Les experts Python semblent s'accorder pour dire que c'est la
façon préférée de résoudre le problème en Python, en écrivant
soit
def foo(n): class acc: def init(self, s): self.s = s def inc(self, i): self.s += i return self.s return acc(n).inc
soit
class foo: def init(self, n): self.n = n def call(self, i): self.n += i return self.n
Je les inclus parce que je ne voudrais pas que les défenseurs de Python
disent que je déformais le langage, mais les deux me semblent plus complexes que la première
version. Vous faites la même chose, vous configurez
un endroit séparé pour stocker l'accumulateur ; c'est juste
un champ dans un objet au lieu de la tête d'une liste.
Et l'utilisation de ces noms de champs spéciaux, réservés, en particulier __call__, semble
un peu un hack.
Dans la rivalité entre Perl et Python, la revendication des
hackers Python semble être que
que Python est une alternative plus élégante à Perl, mais ce
ce cas montre que la puissance est l'élégance ultime :
le programme Perl est plus simple (a moins d'éléments), même si la
syntaxe est un peu plus laide.
Qu'en est-il des autres langages ? Dans les autres langages
mentionnés dans cette conférence - Fortran, C, C++, Java et
Visual Basic - il n'est pas clair si vous pouvez vraiment
résoudre ce problème.
Ken Anderson dit que le code suivant est à peu près aussi proche
que vous pouvez l'obtenir en Java :
public interface Inttoint { public int call(int i); }
public static Inttoint foo(final int n) { return new Inttoint() { int s = n; public int call(int i) { s = s + i; return s; }}; }
Cela ne répond pas complètement aux spécifications car il ne fonctionne que pour
les entiers. Après de nombreux échanges de courriers électroniques avec des hackers Java,
je dirais que l'écriture d'une version correctement polymorphe
qui se comporte comme les exemples précédents se situe entre
sacrément maladroit et impossible. Si quelqu'un veut
en écrire une, je serais très curieux de la voir, mais personnellement,
j'ai abandonné.
Il n'est pas littéralement vrai que vous ne pouvez pas résoudre ce
problème dans d'autres langages, bien sûr. Le fait
que tous ces langages sont équivalents de Turing signifie
que, strictement parlant, vous pouvez écrire n'importe quel programme dans
n'importe lequel d'entre eux. Alors comment feriez-vous ? Dans le cas limite,
en écrivant un interpréteur Lisp
dans le langage le moins puissant.
Cela ressemble à une blague, mais cela arrive si souvent
à des degrés divers dans les grands projets de programmation qu'il
y a un nom pour ce phénomène, la Dixième Règle de Greenspun :
Tout programme C ou Fortran suffisamment
compliqué contient une implémentation lente, informelle et boguée de la moitié de
Common Lisp.
Si vous essayez de résoudre un
problème difficile, la question n'est pas de savoir si vous utiliserez
un langage suffisamment puissant, mais si vous (a)
utiliserez un langage puissant, (b) écrirez un interpréteur de facto
pour l'un d'entre eux, ou (c) deviendrez vous-même un compilateur humain pour l'un d'entre eux.
Nous voyons déjà
cela commencer à se produire dans l'exemple Python, où nous
simulons en fait le code qu'un compilateur
générerait pour mettre en œuvre une variable lexicale.
Cette pratique n'est pas seulement courante, mais institutionnalisée. Par exemple, dans le monde de l'OO, on entend beaucoup parler des "patterns". Je me demande si ces patterns ne sont pas parfois la preuve du cas (c), le compilateur humain, au travail. Quand je vois des patterns dans mes programmes, je considère que c'est un signe de problème. La forme d'un programme ne devrait refléter que le problème qu'il doit résoudre. Toute autre régularité dans le code est un signe, pour moi du moins, que j'utilise des abstractions qui ne sont pas assez puissantes - souvent que je génère manuellement les expansions de quelque macro dont j'ai besoin d'écrire.
**Notes**
Le CPU IBM 704 était à peu près de la taille d'un réfrigérateur, mais beaucoup plus lourd. Le CPU pesait 3150 livres, et les 4 Ko de RAM étaient dans un boîtier séparé pesant 4000 livres de plus. Le Sub-Zero 690, l'un des plus gros réfrigérateurs domestiques, pèse 656 livres.
Steve Russell a également écrit le premier jeu informatique (numérique), Spacewar, en 1962.
Si vous voulez tromper un patron à tête pointue pour qu'il vous laisse écrire un logiciel en Lisp, vous pourriez essayer de lui dire que c'est du XML.
Voici le générateur d'accumulateurs dans d'autres dialectes Lisp :
Scheme : (define (foo n) (lambda (i) (set! n (+ n i)) n)) Goo : (df foo (n) (op incf n _))) Arc : (def foo (n) [++ n _])
Le triste récit d'Erann Gat sur les "meilleures pratiques de l'industrie" chez JPL m'a inspiré pour aborder cette expression généralement mal appliquée.
Peter Norvig a constaté que 16 des 23 modèles dans *Design Patterns* étaient "[invisibles ou plus simples](http://www.norvig.com/design-patterns/)" en Lisp.
Merci aux nombreuses personnes qui ont répondu à mes questions sur divers langages et/ou lu des brouillons de ce texte, notamment Ken Anderson, Trevor Blackwell, Erann Gat, Dan Giffin, Sarah Harlin, Jeremy Hylton, Robert Morris, Peter Norvig, Guy Steele et Anton van Straaten. Ils ne sont en aucun cas responsables des opinions exprimées.
**Liens connexes :**
De nombreuses personnes ont réagi à cette conférence, j'ai donc créé une page supplémentaire pour traiter des questions qu'elles ont soulevées : [Re: Revenge of the Nerds](https://paulgraham.com/icadmore.html).
Cela a également déclenché une discussion approfondie et souvent utile sur la liste de diffusion [LL1](http://www.ai.mit.edu/~gregs/ll1-discuss-archive-html/threads.html). Voir en particulier le mail d'Anton van Straaten sur la compression sémantique.
Certains des messages sur LL1 m'ont amené à essayer d'approfondir le sujet du pouvoir des langages dans [Succinctness is Power](https://paulgraham.com/power.html).
Un ensemble plus important d'implémentations canoniques du [benchmark du générateur d'accumulateurs](https://paulgraham.com/accgen.html) sont rassemblées sur leur propre page.
[Traduction japonaise](http://www.shiro.dreamhost.com/scheme/trans/icad-j.html), [Traduction espagnole](http://kapcoweb.com/p/docs/translations/revenge_of_the_nerds/revenge_of_the_nerds-es.html), [Traduction chinoise](http://flyingapplet.spaces.live.com/blog/cns!F682AFBD82F7E261!375.entry)