La Revanche des Nerds

Vous voulez lancer une startup ? Faites-vous financer par Y Combinator.


Mai 2002

| "Nous voulions les programmeurs C++. Nous avons réussi à en traîner beaucoup à mi-chemin vers Lisp."

  • Guy Steele, co-auteur de la spécification Java

Dans le monde du logiciel, il y a une lutte constante entre les universitaires à la tête pointue et une autre force tout aussi redoutable, les patrons à la tête pointue. Tout le monde sait qui est le patron à la 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 dessin animé, mais connaissent la personne réelle dans leur entreprise sur laquelle il est modelé.

Le patron à la tête pointue combine miraculeusement deux qualités qui sont courantes séparément, mais rarement vues ensemble : (a) il ne connaît absolument rien à 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 à la tête pointue n'a aucune idée de la façon dont ce logiciel doit fonctionner, 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 ? Jetons un coup d'œil à l'intérieur du cerveau du patron à la tête pointue. Ce qu'il pense ressemble à ceci. Java est un standard. Je sais que ça doit l'être, parce que j'en lis tout le temps dans la presse. Puisque c'est un standard, je n'aurai pas d'ennuis 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 est basé sur une hypothèse tacite, et cette hypothèse s'avère fausse. Le patron à la 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 le langage 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 à la tête pointue en 1992 dans quel langage un logiciel devrait être écrit, il aurait répondu avec aussi peu d'hésitation qu'aujourd'hui. Le logiciel devrait être écrit en C++. Mais si les langages sont tous équivalents, pourquoi l'opinion du patron à la 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 précise dans le premier livre blanc de Java que Java a été conçu pour résoudre certains problèmes avec C++. Voilà donc : les langages ne sont pas tous équivalents. Si vous suivez la piste à travers le cerveau du patron à la tête pointue jusqu'à Java, puis remontez l'histoire de Java jusqu'à ses origines, vous vous retrouvez avec une idée qui contredit l'hypothèse de départ.

Alors, qui a raison ? James Gosling, ou le patron à la tête pointue ? Sans surprise, Gosling a 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++ ? 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 à la tête pointue devait penser au problème dans toute sa complexité, son cerveau exploserait. Tant qu'il considère tous les langages équivalents, tout ce qu'il a à faire est de choisir celui qui semble avoir le plus d'élan, et puisque 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 soudainement résoudre deux équations simultanées, essayant de trouver un équilibre optimal entre deux choses qu'il ne connaît pas : l'adéquation relative de la vingtaine de langages principaux 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 trouve de l'autre côté de la porte, il n'est pas surprenant que le patron à la 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 grandement 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 plutôt bon, car c'est le nouveau langage de programmation cool. Ou l'est-il ? Si vous regardez le monde des langages de programmation de loin, il semble que Java soit la dernière nouveauté. (D'assez loin, tout ce que vous pouvez voir est le grand panneau publicitaire clignotant payé par Sun.) Mais si vous regardez ce monde de près, vous constatez qu'il y a des degrés de "coolness". Au sein de la sous-culture des hackers, il existe un autre langage appelé Perl qui est considéré comme beaucoup plus cool que Java. Slashdot, par exemple, est généré par Perl. Je ne pense pas que ces gars-là utiliseraient des Java Server Pages. Mais il existe un autre langage, plus récent, appelé Python, dont les utilisateurs ont tendance à mépriser Perl, et plus attendent dans les coulisses.

Si vous regardez ces langages dans l'ordre, Java, Perl, Python, vous remarquez un schéma intéressant. Du moins, vous remarquez ce schéma si vous êtes un hacker Lisp. Chacun ressemble progressivement 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 à l'époque.

Maintenant, comment cela pourrait-il être vrai ? La technologie informatique n'est-elle pas quelque chose qui change très rapidement ? Je veux dire, en 1958, les ordinateurs étaient des mastodontes de la taille d'un réfrigérateur avec la puissance de traitement d'une montre-bracelet. Comment une technologie aussi ancienne pourrait-elle être pertinente, sans parler d'être 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 langage de programmation est quelque chose que nous utilisons pour dire à un ordinateur quoi faire. McCarthy avait finalement l'intention de développer un langage de programmation dans ce sens, mais le Lisp que nous avons réellement obtenu était basé sur quelque chose de distinct qu'il a fait comme exercice théorique -- un effort pour définir une alternative plus pratique à la Machine de Turing. Comme McCarthy l'a dit 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 est 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.... L'écriture d'eval a nécessité d'inventer une notation représentant les fonctions Lisp comme des données Lisp, et une telle notation a été conçue pour les besoins de l'article sans penser qu'elle serait utilisée pour exprimer des programmes Lisp en pratique.

Ce qui s'est passé ensuite, c'est que, fin 1958, Steve Russell, l'un des étudiants diplômés de McCarthy, a examiné cette définition d'eval et a réalisé que s'il la traduisait en langage machine, le résultat serait un interpréteur Lisp.

Ce fut une grande surprise à l'époque. Voici ce que McCarthy en a dit plus tard dans une interview :

Steve Russell a dit, regarde, pourquoi ne pas programmer cet eval..., et je lui ai dit, ho, ho, tu confonds la théorie et la pratique, cet eval est destiné à la lecture, pas au calcul. Mais il l'a fait. C'est-à-dire qu'il a compilé l'eval de mon article en code machine [IBM] 704, corrigeant les bugs, puis l'a présenté comme un interpréteur Lisp, ce qu'il était certainement. Donc à ce moment-là, Lisp avait essentiellement la forme qu'il a aujourd'hui....

Soudain, en quelques semaines je pense, McCarthy a vu son exercice théorique transformé en un véritable langage de programmation – et un langage plus puissant qu'il ne l'avait prévu.

La courte explication de pourquoi ce langage des années 1950 n'est pas obsolète est qu'il ne s'agissait pas de technologie mais de mathématiques, et les mathématiques ne se périment pas. La bonne chose à comparer Lisp n'est pas le matériel des années 1950, mais, disons, l'algorithme Quicksort, qui a été découvert en 1960 et est toujours le tri 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 de la conception des langages. Lisp était une pièce de théorie qui s'est inopinément transformée en langage de programmation. Fortran a été développé intentionnellement comme un langage de programmation, mais ce que nous considérerions maintenant comme un langage de très bas niveau.

Fortran I, le langage développé en 1956, était un animal très différent du Fortran actuel. Fortran I était pratiquement un 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 branchements. Le Fortran actuel est maintenant sans doute 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 machine. Ces deux arbres ont convergé depuis. Lisp a commencé puissant, et au cours des vingt années suivantes est devenu rapide. Les langages dits grand public ont commencé rapides, et au cours des quarante années suivantes sont progressivement devenus 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 éléments....

Ce Qui Rendait 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 considérées comme acquises, d'autres ne sont vues que dans des langages plus avancés, et deux sont encore uniques à Lisp. Les neuf idées sont, dans l'ordre de leur adoption par le courant dominant,

  1. Conditionnelles. Une conditionnelle est une construction si-alors-sinon. 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.

  2. Un type de fonction. En Lisp, les fonctions sont un type de données tout 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 comme arguments, et ainsi de suite.

  3. Récursion. Lisp a été le premier langage de programmation à la prendre en charge.

  4. Typage dynamique. En Lisp, toutes les variables sont effectivement des pointeurs. Ce sont les valeurs qui ont des types, pas les variables, et l'affectation ou la liaison de variables signifie la copie de pointeurs, pas ce vers quoi ils pointent.

  5. Collecte de déchets (Garbage-collection).

  6. Programmes composés d'expressions. Les programmes Lisp sont des arbres d'expressions, chacune d'elles renvoyant une valeur. Ceci contraste avec Fortran et la plupart des langages suivants, qui distinguent les expressions des instructions.

Il était naturel d'avoir cette distinction dans Fortran I car on ne pouvait pas imbriquer les instructions. Et donc, bien qu'on ait eu besoin d'expressions pour que les mathématiques fonctionnent, il n'y avait aucun intérêt à faire en sorte que quoi que ce soit d'autre renvoie une valeur, car il ne pouvait y avoir rien qui l'attendait.

Cette limitation a disparu avec l'arrivée des langages à structure de blocs, mais il était alors trop tard. La distinction entre expressions et instructions était bien ancrée. Elle s'est propagée de Fortran à Algol, puis à leurs deux descendants.

  1. Un type de symbole. Les symboles sont effectivement des pointeurs vers des chaînes stockées dans une table de hachage. Vous pouvez donc tester l'égalité en comparant un pointeur, au lieu de comparer chaque caractère.

  2. Une notation pour le code utilisant des arbres de symboles et de constantes.

  3. Le langage entier est toujours là. 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 au moment de l'exécution.

L'exécution de code au moment de la lecture permet aux utilisateurs de reprogrammer la syntaxe de Lisp ; l'exécution de code au moment de la compilation est la base des macros ; la compilation au moment de l'exécution est la base de l'utilisation de Lisp comme langage d'extension dans des programmes comme Emacs ; et la lecture au moment de l'exécution permet aux programmes de communiquer en utilisant des s-expressions, une idée récemment réinventée sous forme de XML.

Lorsque 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é par une succession de langages populaires, a progressivement évolué vers Lisp. Les idées 1 à 5 sont maintenant largement répandues. Le numéro 6 commence à apparaître dans le courant dominant. Python a une forme du 7, bien qu'il ne semble pas y avoir de syntaxe pour cela.

Quant au 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 de 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 s'avèrent être responsables à la fois de l'apparence étrange de Lisp et de ses caractéristiques les plus distinctives. Lisp semble étrange non pas tant parce qu'il a une 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 coulisses lorsque d'autres langages sont analysés, et ces arbres sont faits 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 n'importe quoi, d'une abréviation à un compilateur pour un nouveau langage. Si vous voulez vraiment comprendre Lisp, ou simplement élargir vos horizons de programmation, je vous recommanderais d'en apprendre plus sur les macros.

Les macros (au sens Lisp) sont toujours, à ma connaissance, uniques à Lisp. C'est en partie parce que pour avoir des macros, vous devez probablement faire en sorte que votre langage ait l'air aussi étrange que Lisp. Cela peut aussi être parce que si vous ajoutez ce dernier incrément de puissance, vous ne pouvez plus prétendre avoir inventé un nouveau langage, mais seulement un nouveau dialecte de Lisp.

Je le mentionne 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 de Lisp à partir de cela. C'est en fait la qualité distinctive de Lisp : c'est pour que cela soit ainsi que McCarthy a donné à Lisp la forme qu'il a.

Où les Langages Comptent

Alors, supposons que Lisp représente une sorte de limite que les langages grand public approchent asymptotiquement-- cela signifie-t-il que vous devriez réellement l'utiliser pour écrire des logiciels ? Combien 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, sa propre justification ? Le patron à la 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 exigeants du tout. La plupart de la programmation consiste probablement à écrire de petits programmes de "glue", et pour ces petits programmes, vous pouvez utiliser n'importe quel langage que vous connaissez déjà et qui dispose de bonnes bibliothèques pour tout ce dont vous avez besoin. Si vous avez juste besoin de transférer des données d'une application Windows à une autre, bien sûr, utilisez Visual Basic.

Vous pouvez aussi écrire de petits programmes de "glue" en Lisp (je l'utilise comme calculatrice de bureau), mais le plus grand avantage des langages comme 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 que ITA Software licencie à Orbitz. Ces gars sont entrés sur un marché déjà dominé par deux grands concurrents bien établis, Travelocity et Expedia, et semblent les avoir simplement humiliés technologiquement.

Le cœur de l'application d'ITA est un programme Common Lisp de 200 000 lignes qui recherche beaucoup plus de possibilités que leurs concurrents, qui utilisent apparemment encore des techniques de programmation de l'ère des mainframes. (Bien qu'ITA utilise aussi, dans un certain sens, un langage de programmation de l'ère des mainframes.) 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 aucun coût à utiliser des technologies peu courantes. Le patron à la 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 des difficultés à embaucher des programmeurs.

Quelle est l'ampleur de chacun de ces problèmes ? L'importance du premier varie selon que vous avez le contrôle de l'ensemble du système. Si vous écrivez un logiciel qui doit fonctionner sur la machine d'un utilisateur distant, par-dessus un système d'exploitation bogué et fermé (je ne mentionne aucun nom), il peut y avoir des avantages à écrire votre application dans le même langage que l'OS. Mais si vous contrôlez l'ensemble du système et avez le code source de toutes les parties, comme ITA le fait probablement, vous pouvez utiliser les langages que vous voulez. Si une incompatibilité survient, vous pouvez la corriger vous-même.

Dans les applications basées sur serveur, vous pouvez vous permettre d'utiliser les technologies les plus avancées, et je pense que c'est la cause principale de ce que Jonathan Erickson appelle la "renaissance des langages de programmation". C'est pourquoi nous entendons même parler de nouveaux langages comme Perl et Python. Nous n'entendons 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 du bureau vers les serveurs (un avenir auquel même Microsoft semble résigné), il y aura de moins en moins de pression pour utiliser des technologies de milieu de gamme.

Quant aux bibliothèques, leur importance dépend également de l'application. Pour les problèmes 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 où qu'il soit, il est en deçà de tout ce que vous appelleriez probablement une application. Si une entreprise se considère comme étant dans le secteur du logiciel, et qu'elle écrit une application qui sera l'un de ses produits, alors cela impliquera probablement plusieurs hackers et prendra 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 préoccupation du patron à la tête pointue, la difficulté d'embaucher des programmeurs, est à mon avis une fausse piste. Combien de hackers avez-vous besoin d'embaucher, après tout ? Sûrement, 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 on ait jamais entendu parler. Si vous ne trouvez pas 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 diminue 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 susceptibles d'être plus intelligents.

Je ne dis pas que vous ne subirez pas beaucoup de pression pour utiliser ce qui est perçu comme des technologies "standard". Chez Viaweb (maintenant Yahoo Store), nous avons fait sourciller les VCs et les acquéreurs potentiels en utilisant Lisp. Mais nous avons aussi fait sourciller en utilisant des boîtiers Intel génériques comme serveurs au lieu de serveurs "de force industrielle" comme les Suns, pour utiliser une variante Unix open-source alors obscure appelée FreeBSD au lieu d'un véritable OS commercial comme Windows NT, pour ignorer une norme de commerce électronique supposée appelée SET dont personne ne se souvient même plus, et ainsi de suite.

Vous ne pouvez pas laisser les "costards" prendre les 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 pu écrire le logiciel qui leur a donné envie de nous acheter. Ce qui leur semblait une anomalie était en fait une relation de cause à effet.

Si vous lancez une startup, ne concevez pas votre produit pour plaire aux VCs ou aux acquéreurs potentiels. Concevez votre produit pour plaire aux utilisateurs. Si vous gagnez les utilisateurs, tout le reste suivra. Et si vous ne le faites pas, personne ne se souciera de la conformité orthodoxe de vos choix technologiques.

Le Coût d'Être Moyen

Combien perdez-vous en utilisant un langage moins puissant ? Il existe en fait des données à ce sujet.

La mesure de puissance la plus pratique est probablement la taille du code. L'intérêt des langages de haut niveau est de vous donner de plus grandes abstractions-- de plus grandes briques, pour ainsi dire, de sorte que vous n'en ayez pas besoin d'autant pour construire un mur d'une taille donnée. Ainsi, plus le langage est puissant, plus le programme est court (pas simplement 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 ce qu'on appelle la programmation ascendante (bottom-up). Au lieu de simplement écrire votre application dans le langage de base, vous construisez sur le langage de base un langage pour écrire des programmes comme le vôtre, puis vous écrivez votre programme dedans. Le code combiné peut être beaucoup plus court que si vous aviez écrit tout votre programme dans le langage de base-- en effet, 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 du tout besoin de 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 était trois fois plus long dans un autre langage, il faudrait trois fois plus de temps pour l'écrire-- et vous ne pouvez pas contourner cela en embauchant plus de personnes, car au-delà d'une certaine taille, les nouvelles embauches sont en fait une perte nette. Fred Brooks a décrit ce phénomène dans son célèbre livre Le Mythe du Mois-Homme, et tout ce que j'ai vu a eu tendance à confirmer ce qu'il a dit.

Alors, de combien vos programmes sont-ils plus courts si vous les écrivez en Lisp ? La plupart des chiffres que j'ai entendus pour Lisp contre C, par exemple, étaient d'environ 7 à 10 fois. Mais un article récent sur ITA dans le magazine New Architect disait qu'"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 croire ; le logiciel d'ITA comprend beaucoup de C et C++ ainsi que de Lisp, donc ils parlent d'expérience.

Mon hypothèse est que ces multiples ne sont même pas constants. Je pense qu'ils augmentent lorsque vous faites face à des problèmes plus difficiles et aussi lorsque vous avez des programmeurs plus intelligents. Un très bon hacker peut tirer davantage de meilleurs outils.

Comme point de données sur la courbe, en tout cas, si vous deviez concurrencer ITA et choisissiez d'écrire votre logiciel en C, ils seraient capables de développer des logiciels vingt fois plus vite que vous. Si vous passiez un an sur une nouvelle fonctionnalité, ils seraient capables de 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 réellement écrire le programme dans le langage le moins puissant. 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 en tête en même temps.

Donc, quand je dis qu'il faudrait cinq ans au concurrent imaginaire d'ITA pour dupliquer quelque chose qu'ITA pourrait écrire en Lisp en trois mois, je veux dire cinq ans si rien ne va mal. En fait, la façon dont les choses fonctionnent dans la plupart des entreprises, tout projet de développement qui prendrait cinq ans est susceptible de ne jamais être terminé du tout.

J'admets que c'est un cas extrême. Les hackers d'ITA semblent être exceptionnellement intelligents, et C est un langage de très bas niveau. Mais sur un marché concurrentiel, même un différentiel de deux ou trois contre un suffirait à garantir que vous seriez toujours en retard.

Une Recette

C'est le genre de possibilité à laquelle le patron à la tête pointue ne veut même pas penser. Et la plupart d'entre eux ne le font pas. Parce que, vous savez, au final, le patron à la tête pointue ne se soucie pas que son entreprise se fasse botter les fesses, tant que personne ne peut prouver que c'est de sa faute. Le plan le plus sûr pour lui personnellement est de rester proche du centre du troupeau.

Au sein des grandes organisations, l'expression utilisée pour décrire cette approche est "meilleures pratiques de l'industrie". Son but est de protéger le patron à la tête pointue de toute responsabilité : s'il choisit quelque chose qui relève des "meilleures pratiques de l'industrie", et que l'entreprise perd, il ne peut pas être blâmé. Ce n'est pas lui qui a choisi, c'est l'industrie.

Je crois que ce terme était à l'origine utilisé pour décrire les méthodes comptables et ainsi de suite. Ce que cela signifie, en gros, c'est ne faites rien d'étrange. Et en comptabilité, c'est probablement une bonne idée. Les termes "pointe de la technologie" et "comptabilité" ne sonnent pas bien ensemble. Mais lorsque 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 souligné Erann Gat, ce que les "meilleures pratiques de l'industrie" vous apportent en réalité n'est pas le meilleur, mais simplement la moyenne. Lorsqu'une décision vous fait développer des logiciels à une fraction du rythme de concurrents plus agressifs, "meilleure pratique" est un terme impropre.

Nous avons donc ici deux informations que je considère très précieuses. En fait, je le sais par ma propre expérience. Premièrement, les langages varient en puissance. Deuxièmement, la plupart des managers 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 le secteur du logiciel, attaquez-vous simplement au problème le plus difficile que vous puissiez trouver, utilisez le langage le plus puissant que vous puissiez obtenir, et attendez que les patrons à la tête pointue de vos concurrents reviennent à la moyenne.


Annexe : Puissance

Pour illustrer ce que j'entends 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, ce serait (defun foo (n) (lambda (i) (incf n i))) et en Perl 5, sub foo { my ($n) = @_; sub {$n += shift} } ce qui a plus d'éléments que la version Lisp car il faut extraire les paramètres manuellement 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 qu'en général les variables lexicales fonctionnent, vous ne pouvez pas affecter une valeur à un paramètre, vous devez donc créer une nouvelle variable s.

En Javascript, l'exemple est, encore une fois, légèrement plus long, car Javascript conserve la distinction entre les instructions et les expressions, vous avez donc besoin d'instructions return explicites pour renvoyer des valeurs : function foo(n) { return function (i) { return n += i } } (Pour être juste, Perl conserve également cette distinction, mais la gère à la manière typique de Perl en vous permettant d'omettre les return.)

Si vous essayez de traduire le code Lisp/Perl/Smalltalk/Javascript en Python, vous rencontrez certaines limitations. Parce que Python ne prend pas entièrement 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 celui-ci (sauf si le corps n'est qu'une seule expression), vous devez donc 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 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 intuition est qu'ils le feront probablement un jour. (Mais s'ils ne veulent pas attendre que Python évolue jusqu'à devenir Lisp, ils pourraient toujours simplement...)

Dans les langages orientés objet, vous pouvez, dans une mesure limitée, simuler une fermeture (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 oblige le programmeur à effectuer le type d'analyse de code qui serait effectué par le compilateur dans un langage avec un support complet pour 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 sur le fait que c'est la manière 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 ou class foo: def init(self, n): self.n = n def call(self, i): self.n += i return self.n J'inclus ces exemples car je ne voudrais pas que les défenseurs de Python disent que je déforme le langage, mais les deux me semblent plus complexes que la première version. Vous faites la même chose, en mettant en place un endroit séparé pour contenir 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 et réservés, en particulier __call__, semble un peu une bidouille.

Dans la rivalité entre Perl et Python, l'affirmation des hackers Python semble être que Python est une alternative plus élégante à Perl, mais 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 discussion-- Fortran, C, C++, Java et Visual Basic-- il n'est pas clair si vous pouvez réellement résoudre ce problème. Ken Anderson dit que le code suivant est à peu près ce que vous pouvez obtenir de plus proche 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 respecte pas la spécification car cela ne fonctionne que pour les entiers. Après de nombreux échanges d'e-mails avec des hackers Java, je dirais qu'écrire une version correctement polymorphe qui se comporte comme les exemples précédents est quelque part 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 soient équivalents à Turing signifie que, à proprement parler, 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 moins puissant.

Cela ressemble à une blague, mais cela arrive si souvent à des degrés divers dans les grands projets de programmation qu'il existe 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 ad hoc, informellement spécifiée, pleine de bugs et lente 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 allez (a) utiliser un langage puissant, (b) écrire un interpréteur de facto pour l'un d'eux, ou (c) devenir vous-même un compilateur humain pour l'un d'eux. Nous voyons cela déjà commencer à se produire dans l'exemple Python, où nous simulons en fait le code qu'un compilateur générerait pour implémenter une variable lexicale.

Cette pratique est non seulement courante, mais institutionnalisée. Par exemple, dans le monde OO, on entend beaucoup parler de "patterns". Je me demande si ces patterns ne sont pas parfois la preuve du cas (c), le compilateur humain, à l'œuvre. Quand je vois des patterns dans mes programmes, je considère cela comme 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 à la main les expansions d'une macro que je dois écrire.

Notes

  • Le CPU IBM 704 avait à peu près la taille d'un réfrigérateur, mais était beaucoup plus lourd. Le CPU pesait 3150 livres, et les 4K de RAM étaient dans une boîte séparée pesant 4000 livres supplémentaires. Le Sub-Zero 690, l'un des plus grands réfrigérateurs domestiques, pèse 656 livres.

  • Steve Russell a également écrit le premier jeu vidéo (numérique), Spacewar, en 1962.

  • Si vous voulez piéger un patron à la tête pointue pour qu'il vous laisse écrire des logiciels 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 _])

  • La triste histoire d'Erann Gat sur les "meilleures pratiques de l'industrie" au JPL m'a inspiré à aborder cette expression généralement mal appliquée.

  • Peter Norvig a constaté que 16 des 23 patterns dans Design Patterns étaient "invisibles ou plus simples" en Lisp.

  • Merci aux nombreuses personnes qui ont répondu à mes questions sur divers langages et/ou lu les brouillons de cet article, y compris 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.

Articles Liés :

De nombreuses personnes ont répondu à cette discussion, j'ai donc mis en place une page supplémentaire pour traiter les questions qu'elles ont soulevées : Re: La Revanche des Nerds.

Cela a également déclenché une discussion approfondie et souvent utile sur la liste de diffusion LL1. Voir en particulier le message d'Anton van Straaten sur la compression sémantique.

Certains des messages sur LL1 m'ont amené à approfondir le sujet de la puissance des langages dans La Concision est Puissance.

Un ensemble plus large d'implémentations canoniques du benchmark du générateur d'accumulateurs est rassemblé sur sa propre page.

Traduction Japonaise, Traduction Espagnole, Traduction Chinoise