LA CONCISION EST LE POUVOIR
OriginalMai 2002
"La quantité de sens compressée dans un petit espace par les signes algébriques est une autre circonstance qui facilite les raisonnements que nous sommes habitués à mener à l'aide de ceux-ci."
- Charles Babbage, cité dans la conférence de Turing d'Iverson
Dans la discussion sur les problèmes soulevés par Revenge of the Nerds sur la liste de diffusion LL1, Paul Prescod a écrit quelque chose qui m'est resté en tête.
Le but de Python est la régularité et la lisibilité, pas la concision.
À première vue, cela semble être une affirmation assez damnable à propos d'un langage de programmation. Autant que je sache, la concision = le pouvoir. Si c'est le cas, alors en substituant, on obtient
Le but de Python est la régularité et la lisibilité, pas le pouvoir.
et cela ne semble pas être un compromis (si c'est un compromis) que vous voudriez faire. Ce n'est pas loin de dire que le but de Python n'est pas d'être efficace en tant que langage de programmation.
La concision = le pouvoir ? Cela me semble être une question importante, peut-être la question la plus importante pour quiconque s'intéresse à la conception de langages, et une question qu'il serait utile de confronter directement. Je ne suis pas encore sûr que la réponse soit un simple oui, mais cela semble être une bonne hypothèse pour commencer.
Hypothèse
Mon hypothèse est que la concision est le pouvoir, ou est suffisamment proche que, sauf dans des exemples pathologiques, vous pouvez les traiter comme identiques.
Il me semble que la concision est ce pour quoi les langages de programmation sont faits. Les ordinateurs seraient tout aussi heureux d'être informés de ce qu'il faut faire directement en langage machine. Je pense que la principale raison pour laquelle nous prenons la peine de développer des langages de haut niveau est d'obtenir un effet de levier, afin de pouvoir dire (et plus important encore, penser) en 10 lignes d'un langage de haut niveau ce qui nécessiterait 1000 lignes de langage machine. En d'autres termes, le principal intérêt des langages de haut niveau est de réduire la taille du code source.
Si la réduction de la taille du code source est le but des langages de haut niveau, et que la puissance de quelque chose est la façon dont il atteint son but, alors la mesure de la puissance d'un langage de programmation est la façon dont il réduit la taille de vos programmes.
Inversement, un langage qui ne réduit pas la taille de vos programmes est en train de faire un mauvais travail de ce que les langages de programmation sont censés faire, comme un couteau qui ne coupe pas bien, ou une impression illisible.
Métriques
Petit en quel sens alors ? La mesure la plus courante de la taille du code est le nombre de lignes de code. Mais je pense que cette métrique est la plus courante parce qu'elle est la plus facile à mesurer. Je ne pense pas que quiconque croit vraiment que c'est le véritable test de la longueur d'un programme. Différents langages ont des conventions différentes quant à la quantité de code que vous devriez mettre sur une ligne ; en C, beaucoup de lignes ne contiennent rien d'autre qu'un délimiteur ou deux.
Un autre test facile est le nombre de caractères dans un programme, mais ce n'est pas très bon non plus ; certains langages (Perl, par exemple) utilisent simplement des identifiants plus courts que d'autres.
Je pense qu'une meilleure mesure de la taille d'un programme serait le nombre d'éléments, où un élément est tout ce qui serait un nœud distinct si vous dessiniez un arbre représentant le code source. Le nom de une variable ou une fonction est un élément ; un entier ou un nombre à virgule flottante est un élément ; un segment de texte littéral est un élément ; un élément d'un motif, ou une directive de formatage, est un élément ; un nouveau bloc est un élément. Il y a des cas limites (est-ce que -5 est deux éléments ou un ?) mais je pense que la plupart d'entre eux sont les mêmes pour chaque langage, donc ils n'affectent pas beaucoup les comparaisons.
Cette métrique a besoin d'être affinée, et elle pourrait nécessiter une interprétation dans le cas de langages spécifiques, mais je pense qu'elle essaie de mesurer la bonne chose, qui est le nombre de parties qu'un programme possède. Je pense que l'arbre que vous dessineriez dans cet exercice est ce que vous devez faire dans votre tête pour concevoir le programme, et donc sa taille est proportionnelle à la quantité de travail que vous devez faire pour l'écrire ou le lire.
Conception
Ce type de métrique nous permettrait de comparer différents langages, mais ce n'est pas, du moins pour moi, sa principale valeur. La principale valeur du test de concision est de servir de guide dans la conception des langages. La comparaison la plus utile entre les langages est celle qui existe entre deux variantes potentielles du même langage. Que puis-je faire dans le langage pour rendre les programmes plus courts ?
Si la charge conceptuelle de un programme est proportionnelle à sa complexité, et qu'un programmeur donné peut tolérer une charge conceptuelle fixe, alors c'est la même chose que de demander, que puis-je faire pour permettre aux programmeurs d'en faire le plus possible ? Et cela me semble identique à se demander, comment puis-je concevoir un bon langage ?
(Incidemment, rien ne rend plus évident que la vieille blague "tous les langages sont équivalents" est fausse que la conception de langages. Lorsque vous concevez un nouveau langage, vous comparez constamment deux langages - le langage si je faisais x, et si je ne le faisais pas - pour décider lequel est le meilleur. Si c'était vraiment une question sans signification, vous pourriez aussi bien tirer à pile ou face.)
Viser la concision semble être un bon moyen de trouver de nouvelles idées. Si vous pouvez faire quelque chose qui rend de nombreux programmes différents plus courts, ce n'est probablement pas une coïncidence : vous avez probablement découvert une nouvelle abstraction utile. Vous pourriez même être en mesure d'écrire un programme pour vous aider en recherchant dans le code source des motifs répétés. Parmi les autres langages, ceux qui ont la réputation d'être concis seraient ceux vers lesquels il faudrait se tourner pour trouver de nouvelles idées : Forth, Joy, Icon.
Comparaison
La première personne à écrire sur ces questions, à ma connaissance, était Fred Brooks dans le Mois de l'homme-mois mythique. Il a écrit que les programmeurs semblaient générer à peu près la même quantité de code par jour, quel que soit le langage. Lorsque j'ai lu cela pour la première fois dans ma vingtaine, cela m'a beaucoup surpris et semblait avoir des implications énormes. Cela signifiait que (a) la seule façon d'obtenir des logiciels écrits plus rapidement était d'utiliser un langage plus concis, et (b) quelqu'un qui prenait la peine de le faire pouvait laisser ses concurrents qui ne le faisaient pas dans la poussière.
L'hypothèse de Brooks, si elle est vraie, semble être au cœur même du piratage. Au cours des années qui ont suivi, j'ai prêté une attention particulière à toutes les preuves que j'ai pu obtenir sur la question, des études formelles aux anecdotes sur des projets individuels. Je n'ai rien vu qui le contredise.
Je n'ai pas encore vu de preuves qui me semblent concluantes, et je ne m'attends pas à en voir. Des études comme la comparaison de langages de programmation de Lutz Prechelt, tout en générant le type de résultats que j'attendais, ont tendance à utiliser des problèmes qui sont trop courts pour être des tests significatifs. Un meilleur test d'un langage est ce qui se passe dans les programmes qui prennent un mois à écrire. Et le seul vrai test, si vous croyez comme moi que le principal objectif d'un langage est d'être bon pour penser (plutôt que de simplement dire à un ordinateur quoi faire une fois que vous avez pensé à cela) est ce que vous pouvez écrire de nouveau dans celui-ci. Donc, toute comparaison de langages où vous devez répondre à une spécification prédéfinie teste légèrement la mauvaise chose.
Le véritable test d'un langage est la façon dont vous pouvez découvrir et résoudre de nouveaux problèmes, pas la façon dont vous pouvez l'utiliser pour résoudre un problème que quelqu'un d'autre a déjà formulé. Ces deux critères sont assez différents. Dans l'art, des médiums comme la broderie et la mosaïque fonctionnent bien si vous savez à l'avance ce que vous voulez faire, mais sont absolument nuls si vous ne le savez pas. Lorsque vous voulez découvrir l'image au fur et à mesure que vous la créez - comme vous devez le faire avec tout ce qui est aussi complexe qu'une image d'une personne, par exemple - vous devez utiliser un médium plus fluide comme le crayon ou l'encre de Chine ou la peinture à l'huile. Et en effet, la façon dont les tapisseries et les mosaïques sont faites dans la pratique est de faire d'abord un tableau, puis de le copier. (Le mot "cartoon" était à l'origine utilisé pour décrire un tableau destiné à cette fin).
Ce que cela signifie, c'est que nous ne sommes probablement jamais en mesure d'avoir des comparaisons précises de la puissance relative des langages de programmation. Nous aurons des comparaisons précises, mais pas précises. En particulier, les études explicites dans le but de comparer les langages, parce qu'elles utiliseront probablement de petits problèmes, et utiliseront nécessairement des problèmes prédéfinis, auront tendance à sous-estimer la puissance des langages les plus puissants.
Les rapports du terrain, bien qu'ils soient nécessairement moins précis que les études "scientifiques", sont susceptibles d'être plus significatifs. Par exemple, Ulf Wiger d'Ericsson a fait une étude qui a conclu qu'Erlang était 4 à 10 fois plus concis que C++, et proportionnellement plus rapide à développer des logiciels :
Les comparaisons entre les projets de développement internes à Ericsson indiquent une productivité similaire en lignes/heure, y compris toutes les phases de développement logiciel, plutôt indépendamment du langage (Erlang, PLEX, C, C++ ou Java) qui a été utilisé. Ce qui différencie les différents langages devient alors le volume du code source.
L'étude traite également explicitement d'un point qui était seulement implicite dans le livre de Brooks (puisqu'il mesurait les lignes de code débogué) : les programmes écrits dans des langages plus puissants ont tendance à avoir moins de bogues. Cela devient une fin en soi, peut-être plus importante que la productivité des programmeurs, dans des applications comme les commutateurs réseau.
Le test de goût
En fin de compte, je pense que vous devez suivre votre instinct. Que ressentez-vous à programmer dans le langage ? Je pense que la façon de trouver (ou de concevoir) le meilleur langage est de devenir hypersensible à la façon dont un langage vous permet de penser, puis de choisir/concevoir le langage qui vous semble le meilleur. Si une fonctionnalité du langage est maladroite ou restrictive, ne vous inquiétez pas, vous le saurez.
Une telle hypersensibilité aura un coût. Vous constaterez que vous ne pouvez pas supporter de programmer dans des langages maladroits. Je trouve qu'il est insupportablement restrictif de programmer dans des langages sans macros, tout comme quelqu'un qui a l'habitude du typage dynamique trouve qu'il est insupportablement restrictif de devoir revenir à la programmation dans un langage où vous devez déclarer le type de chaque variable, et ne pouvez pas faire une liste d'objets de types différents.
Je ne suis pas le seul. Je connais de nombreux pirates Lisp à qui cela est arrivé. En fait, la mesure la plus précise de la puissance relative des langages de programmation pourrait être le pourcentage de personnes qui connaissent le langage qui accepteront n'importe quel emploi où elles pourront utiliser ce langage, quel que soit le domaine d'application.
Restrictivité
Je pense que la plupart des pirates savent ce que cela signifie pour un langage de se sentir restrictif. Que se passe-t-il lorsque vous ressentez cela ? Je pense que c'est la même sensation que vous ressentez lorsque la rue que vous voulez prendre est barrée, et que vous devez prendre un long détour pour arriver là où vous vouliez aller. Il y a quelque chose que vous voulez dire, et le langage ne vous le permet pas.
Ce qui se passe vraiment ici, je pense, c'est qu'un langage restrictif est un langage qui n'est pas assez concis. Le problème n'est pas simplement que vous ne pouvez pas dire ce que vous aviez prévu de dire. C'est que le détour que le langage vous fait prendre est plus long. Essayez cette expérience de pensée. Supposons qu'il y ait un programme que vous vouliez écrire, et que le langage ne vous permette pas de l'exprimer comme vous l'aviez prévu, mais vous oblige plutôt à écrire le programme d'une autre manière qui soit plus courte. Pour moi au moins, cela ne me semblerait pas très restrictif. Ce serait comme si la rue que vous vouliez prendre était barrée, et que le policier à l'intersection vous dirigeait vers un raccourci au lieu d'un détour. Génial !
Je pense que la plupart (quatre-vingt-dix pour cent ?) de la sensation de restrictivité vient d'être obligé de rendre le programme que vous écrivez dans le langage plus long que celui que vous avez en tête. La restrictivité est principalement un manque de concision. Donc, lorsqu'un langage vous semble restrictif, cela signifie (la plupart du temps) qu'il n'est pas assez concis, et lorsqu'un langage n'est pas concis, il vous semblera restrictif.
Lisibilité
La citation par laquelle j'ai commencé mentionne deux autres qualités, la régularité et la lisibilité. Je ne suis pas sûr de ce qu'est la régularité, ou de quel avantage, le cas échéant, un code qui est régulier et lisible a sur un code qui est simplement lisible. Mais je pense que je sais ce que l'on entend par lisibilité, et je pense que cela est également lié à la concision.
Nous devons faire attention ici à distinguer la lisibilité de une ligne de code individuelle et la lisibilité de l'ensemble du programme. C'est la seconde qui compte. Je suis d'accord pour dire qu'une ligne de Basic est susceptible d'être plus lisible qu'une ligne de Lisp. Mais un programme écrit en Basic est va avoir plus de lignes que le même programme écrit en Lisp (surtout une fois que vous passez en Greenspunland). Le effort total de lecture du programme Basic sera certainement plus important.
effort total = effort par ligne x nombre de lignes
Je ne suis pas aussi sûr que la lisibilité soit directement proportionnelle à la concision que je le suis pour le pouvoir, mais certainement la concision est un facteur (au sens mathématique ; voir l'équation ci-dessus) dans la lisibilité. Il se peut donc que ce ne soit même pas significatif de dire que le but d'un langage est la lisibilité, pas la concision ; cela pourrait ressembler à dire que le but était la lisibilité, pas la lisibilité.
Ce que la lisibilité par ligne signifie, pour l'utilisateur qui rencontre le langage pour la première fois, c'est que le code source semblera non menaçant. Donc la lisibilité par ligne pourrait être une bonne décision marketing, même si c'est une mauvaise décision de conception. C'est isomorphe à la technique très réussie qui consiste à laisser les gens payer en plusieurs fois : au lieu de les effrayer avec un prix initial élevé, vous leur dites le faible paiement mensuel. Les plans de paiement sont une perte nette pour l'acheteur, cependant, tout comme la simple lisibilité par ligne est probablement pour le programmeur. L'acheteur va faire beaucoup de ces faibles paiements ; et le programmeur va lire beaucoup de ces lignes individuellement lisibles.
Ce compromis est antérieur aux langages de programmation. Si vous êtes habitué à lire des romans et des articles de journaux, votre première expérience de lecture d'un article de mathématiques peut être décourageante. Il peut falloir une demi-heure pour lire une seule page. Et pourtant, je suis assez sûr que la notation n'est pas le problème, même si elle peut sembler l'être. L'article de mathématiques est difficile à lire parce que les idées sont difficiles. Si vous exprimiez les mêmes idées en prose (comme les mathématiciens devaient le faire avant de développer des notations concises), elles ne seraient pas plus faciles à lire, car l'article prendrait la taille d'un livre.
Dans quelle mesure ?
Un certain nombre de personnes ont rejeté l'idée que la concision = le pouvoir. Je pense qu'il serait plus utile, au lieu de simplement affirmer qu'elles sont les mêmes ou ne le sont pas, de demander : dans quelle mesure la concision = le pouvoir ? Parce que clairement la concision est une grande partie de ce pour quoi les langages de haut niveau sont faits. Si ce n'est pas tout ce pour quoi ils sont faits, alors à quoi servent-ils d'autre, et quelle est l'importance relative de ces autres fonctions ?
Je ne propose pas cela simplement pour rendre le débat plus civilisé. Je veux vraiment savoir la réponse. Quand, si jamais, un langage est-il trop concis pour son propre bien ?
L'hypothèse par laquelle j'ai commencé était que, sauf dans des exemples pathologiques, je pensais que la concision pouvait être considérée comme identique au pouvoir. Ce que je voulais dire, c'est que dans n'importe quel langage que quelqu'un pourrait concevoir, ils seraient identiques, mais que si quelqu'un voulait concevoir un langage explicitement pour réfuter cette hypothèse, il pourrait probablement le faire. Je ne suis même pas sûr de cela, en fait.
Langages, pas programmes
Nous devons être clairs que nous parlons de la concision des langages, pas des programmes individuels. Il est certainement possible que des programmes individuels soient écrits de manière trop dense.
J'ai écrit à ce sujet dans On Lisp. Une macro complexe peut devoir économiser plusieurs fois sa propre longueur pour être justifiée. Si l'écriture d'une macro velue pouvait vous faire économiser dix lignes de code chaque fois que vous l'utilisez, et que la macro elle-même fait dix lignes de code, alors vous obtenez une économie nette en lignes si vous l'utilisez plus d'une fois. Mais cela pourrait quand même être une mauvaise décision, parce que les définitions de macros sont plus difficiles à lire que le code ordinaire. Vous pourriez devoir utiliser la macro dix ou vingt fois avant qu'elle ne produise une amélioration nette de la lisibilité.
Je suis sûr que chaque langage a de tels compromis (bien que je soupçonne que les enjeux augmentent à mesure que le langage devient plus puissant). Chaque programmeur doit avoir vu du code qu'une personne intelligente a rendu marginalement plus court en utilisant des astuces de programmation douteuses.
Il n'y a donc pas d'argument à ce sujet - du moins, pas de ma part. Des programmes individuels peuvent certainement être trop concis pour leur propre bien. La question est, un langage peut-il l'être ? Un langage peut-il obliger les programmeurs à écrire du code qui est court (en éléments) au détriment de la lisibilité globale ?
Une raison pour laquelle il est difficile d'imaginer qu'un langage soit trop concis est que si il y avait une façon excessivement compacte de formuler quelque chose, il y aurait probablement aussi une façon plus longue. Par exemple, si vous pensiez que les programmes Lisp utilisant beaucoup de macros ou de fonctions d'ordre supérieur étaient trop denses, vous pourriez, si vous le préfériez, écrire du code qui soit isomorphe à Pascal. Si vous ne voulez pas exprimer la factorielle en Arc comme un appel à une fonction d'ordre supérieur
(rec zero 1 * 1-)
vous pouvez également écrire une définition récursive :
(rfn fact (x) (if (zero x) 1 (* x (fact (1- x)))))
Bien que je ne puisse pas penser à des exemples du haut de la tête, je suis intéressé par la question de savoir si un langage pourrait être trop concis. Y a-t-il des langages qui vous obligent à écrire du code d'une manière qui est serrée et incompréhensible ? Si quelqu'un a des exemples, je serais très intéressé de les voir.
(Rappel : Ce que je recherche, ce sont des programmes qui sont très denses selon la métrique des "éléments" esquissée ci-dessus, pas simplement des programmes qui sont courts parce que les délimiteurs peuvent être omis et que tout a un nom d'un caractère.)