LA CONCISION EST LE POUVOIR
OriginalMai 2002
"La quantité de sens comprimée dans un petit espace par les signes algébriques est une autre circonstance qui facilite les raisonnements que nous avons l'habitude de mener avec leur aide."
- Charles Babbage, cité dans la conférence du prix Turing d'Iverson
Dans la discussion sur les questions soulevées par Revenge of the Nerds sur la liste de diffusion LL1, Paul Prescod a écrit quelque chose qui m'est resté en tête.
L'objectif de Python est la régularité et la lisibilité, pas la concision.
À première vue, cela semble être une chose assez accablante à dire d'un langage de programmation. Autant que je puisse en juger, la concision = le pouvoir. Si c'est le cas, alors en substituant, on obtient
L'objectif de Python est la régularité et la lisibilité, pas le pouvoir.
et cela ne semble pas être un compromis (si c'en est un) que l'on voudrait faire. Ce n'est pas loin de dire que l'objectif 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 plus importante pour quiconque s'intéresse à la conception de langages, et il serait utile de l'aborder 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 en est si proche que, sauf dans des exemples pathologiques, on peut 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 levier, afin que nous puissions 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, l'objectif principal des langages de haut niveau est de réduire la taille du code source.
Si un code source plus petit est l'objectif des langages de haut niveau, et que la puissance de quelque chose est la mesure dans laquelle il atteint son objectif, 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 ne fait pas un bon 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
Mais petit dans quel sens ? 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 quelqu'un croie vraiment que c'est le vrai test de la longueur d'un programme. Différents langages ont des conventions différentes sur la quantité à mettre sur une ligne ; en C, beaucoup de lignes n'ont 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 tout simplement des identificateurs 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 d' une variable ou d'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 format 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 tous les langages, donc ils n'affectent pas beaucoup les comparaisons.
Cette métrique a besoin d'être développé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, à savoir le nombre de parties dont un programme est composé. Je pense que l'arbre que vous dessineriez dans cette 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 en tant que guide dans la conception de langages. La comparaison la plus utile entre les langages est 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 d'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 se demander, que puis-je faire pour permettre aux programmeurs de faire le plus possible ? Et il me semble que c'est identique à se demander, comment puis-je concevoir un bon langage ?
(Soit dit en passant, rien ne rend plus évident que le vieux dicton "tous les langages sont équivalents" est faux 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 cette question était vraiment sans importance, vous pourriez aussi bien lancer une pièce de monnaie.)
Viser la concision semble être une bonne façon de trouver de nouvelles idées. Si vous pouvez faire quelque chose qui rend de nombreux programmes 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 aider en recherchant des motifs répétés dans le code source. Parmi d'autres langages, ceux réputés pour leur concision seraient ceux à examiner pour de nouvelles idées : Forth, Joy, Icon.
Comparaison
La première personne à avoir écrit sur ces questions, à ma connaissance, était Fred Brooks dans The Mythical Man Month. 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 mes vingt ans, cela a été une grande surprise pour moi et cela semblait avoir d'énormes implications. Cela signifiait que (a) le seul moyen d'écrire des logiciels plus rapidement était d'utiliser un langage plus concis, et (b) quelqu'un qui prendrait la peine de le faire pourrait laisser ses concurrents qui ne le feraient pas loin derrière.
L'hypothèse de Brooks, si elle est vraie, semble être au cœur même du hacking. Au fil des années, j'ai prêté une grande attention à toute preuve 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 semblaient concluantes, et je ne m'y attends pas. Des études comme la comparaison des langages de programmation de Lutz Prechelt, bien qu'elles génèrent le type de résultats que j'attendais, ont tendance à utiliser des problèmes 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 but principal d'un langage est d'être bon à penser (plutôt que de simplement dire à un ordinateur ce qu'il faut faire une fois que vous y avez pensé), est ce que vous pouvez écrire de nouveau en lui. Donc toute comparaison de langages où vous devez répondre à une spécification prédéfinie teste légèrement la mauvaise chose.
Le vrai test d'un langage est de savoir à quel point vous pouvez découvrir et résoudre de nouveaux problèmes, pas à quel point vous pouvez l'utiliser pour résoudre un problème que quelqu'un d'autre a déjà formulé. Ces deux critères sont tout à fait différents. En art, des médias comme la broderie et la mosaïque fonctionnent bien si vous savez d'avance ce que vous voulez faire, mais sont absolument horribles si ce n'est pas le cas. Lorsque vous voulez découvrir l'image au fur et à mesure que vous la faites - comme vous devez le faire avec quelque chose d'aussi complexe qu'une image d'une personne, par exemple - vous devez utiliser un médium plus fluide comme le crayon ou le lavis à l'encre ou la peinture à l'huile. Et en effet, la façon dont les tapisseries et les mosaïques sont faites en pratique est de faire d'abord une peinture, puis de la copier. (Le mot "cartoon" a été à l'origine utilisé pour décrire une peinture destinée à cet usage).
Cela signifie que nous n'aurons jamais de 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 devront nécessairement utiliser des problèmes prédéfinis, auront tendance à sous-estimer la puissance des langages les plus puissants.
Les rapports du terrain, bien qu'ils seront 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 pour développer des logiciels :
Comparaisons entre les projets de développement internes d'Ericsson indiquent une productivité similaire en lignes/heure, y compris toutes les phases du développement logiciel, assez indépendamment de la langue utilisée (Erlang, PLEX, C, C++ ou Java). Ce qui différencie alors les différentes langues devient le volume du code source.
L'étude traite également explicitement d'un point qui n'était qu'implicite dans le livre de Brooks (puisqu'il mesurait les lignes de code déboguées) : les programmes écrits dans des langages plus puissants ont tendance à avoir moins de bugs. Cela devient une fin en soi, potentiellement plus importante que la productivité des programmeurs, dans des applications comme les commutateurs réseau.
Le test de dégustation
En fin de compte, je pense que vous devez vous fier à votre instinct. Que ressent-il à programmer dans la langue ? Je pense que la meilleure façon de trouver (ou de concevoir) la meilleure langue est de devenir hypersensible à la façon dont une langue vous permet de penser, puis de choisir/concevoir la langue qui vous convient le mieux. Si une fonctionnalité de la langue est maladroite ou restrictive, ne vous en faites pas, vous le saurez.
Une telle hypersensibilité aura un coût. Vous constaterez que vous ne pouvez pas supporter de programmer dans des langues maladroites. Je le trouve incroyablement restrictif de programmer dans des langues sans macros, tout comme quelqu'un habitué à la programmation dynamique le trouve incroyablement restrictif de devoir revenir à la programmation dans une langue 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 hackers 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 la langue et qui accepteront n'importe quel emploi où elles pourront utiliser cette langue, quel que soit le domaine d'application.
Caractère restrictif
Je pense que la plupart des hackers savent ce que signifie pour une langue d'être restrictive. Que se passe-t-il quand vous ressentez cela ? Je pense que c'est le même sentiment que lorsque la rue que vous vouliez emprunter est bloquée et que vous devez faire un long détour pour atteindre votre destination. Il y a quelque chose que vous voulez dire, et la langue ne vous le permet pas.
Ce qui se passe réellement ici, je pense, c'est qu'une langue restrictive est une langue qui n'est pas assez concise. Le problème n'est pas simplement que vous ne pouvez pas dire ce que vous aviez prévu. C'est que le détour que la langue vous oblige à faire est plus long. Essayez cette expérience de pensée. Supposons qu'il y ait un programme que vous vouliez écrire, et que la langue ne vous permette pas de l'exprimer de la manière que vous aviez prévue, mais vous oblige à écrire le programme d'une autre manière qui serait plus courte. Pour moi en tout cas, cela ne semblerait pas très restrictif. Ce serait comme si la rue que vous vouliez emprunter était bloquée et que le policier au carrefour vous dirigeait vers un raccourci au lieu d'un détour. Génial !
Je pense que la plupart (quatre-vingt-dix pour cent ?) du sentiment de caractère restrictif provient du fait d'être forcé de rendre le programme que vous écrivez dans la langue plus long que celui que vous avez dans votre tête. Le caractère restrictif est principalement un manque de concision. Donc, quand une langue vous semble restrictive, cela signifie (principalement) qu'elle n'est pas assez concise, et quand une langue n'est pas concise, elle vous semblera restrictive.
Lisibilité
La citation avec 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é, ni quel avantage, le cas échéant, un code régulier et lisible aurait sur un code simplement lisible. Mais je pense savoir ce qu'on entend par lisibilité, et je pense que cela est également lié à la concision.
Nous devons faire attention ici à distinguer la lisibilité d'une ligne de code individuelle et la lisibilité de l'ensemble du programme. C'est la deuxième qui compte. Je conviens qu'une ligne de Basic est probablement plus lisible qu'une ligne de Lisp. Mais un programme écrit en Basic aura plus de lignes que le même programme écrit en Lisp (surtout une fois qu'on entre dans le pays de Greenspun). L'effort total de lecture du programme Basic sera sûrement 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 comme je le suis que la puissance l'est, mais la concision est certainement un facteur (au sens mathématique ; voir l'équation ci-dessus) de la lisibilité. Donc, il ne serait peut-être même pas significatif de dire que l'objectif d'une langue est la lisibilité, pas la concision ; ce serait comme dire que l'objectif était la lisibilité, pas la lisibilité.
Que signifie la lisibilité par ligne pour l'utilisateur qui découvre le langage pour la première fois, c'est que le code source aura l'air inoffensif. Donc, la lisibilité par ligne pourrait être une bonne décision de marketing, même si c'est une mauvaise décision de conception. C'est isomorphe à la technique très réussie de laisser les gens payer par versements : au lieu de les effrayer avec un prix élevé à l'avance, vous leur dites le faible paiement mensuel. Les plans de paiement par versements sont une perte nette pour l'acheteur, cependant, tout comme la simple lisibilité par ligne l'est probablement pour le programmeur.
L'acheteur va faire beaucoup, beaucoup de ces petits paiements ; et le programmeur va lire beaucoup de ces lignes individuellement lisibles.
Ce compromis précède les langages de programmation. Si vous avez l'habitude de 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 pourrait vous falloir une demi-heure pour lire une seule page. Et pourtant, je suis à peu près 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 ont dû le faire avant d'avoir évolué vers 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 = la puissance. Je pense qu'il serait plus utile, au lieu d'argumenter simplement qu'elles sont les mêmes ou ne le sont pas, de se demander : dans quelle mesure la concision = la puissance ? Car il est clair que la concision est une grande partie de ce pour quoi les langages de haut niveau sont conçus. S'il ne s'agit pas de tout ce pour quoi ils sont conçus, alors qu'est-ce d'autre et quelle est l'importance relative de ces autres fonctions ?
Je ne propose pas cela seulement pour rendre le débat plus civilisé. Je veux vraiment connaître la réponse. Quand, le cas échéant, un langage est-il trop concis pour son propre bien ?
L'hypothèse avec laquelle j'ai commencé était que, sauf dans des exemples pathologiques, je pensais que la concision pouvait être considérée comme identique à la puissance. Ce que je voulais dire, c'est que dans n'importe quel langage que quelqu'un concevrait, elles seraient identiques, mais que si quelqu'un voulait concevoir un langage explicitement pour réfuter cette hypothèse, il pourrait probablement le faire. Je n'en suis même pas sûr, en fait.
Langages, pas programmes
Nous devrions être clairs sur le fait que nous parlons de la concision des langages, pas de celle des programmes individuels. Il est certainement possible que des programmes individuels soient écrits trop densément.
J'ai écrit à ce sujet dans On Lisp. Une macro complexe peut devoir économiser beaucoup plus que sa propre longueur pour être justifiée. Si l'écriture d'une macro complexe pouvait vous faire économiser dix lignes de code à chaque fois que vous l'utilisez, et que la macro fait elle-même 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, car les définitions de macros sont plus difficiles à lire que le code ordinaire. Vous devriez peut-être l'utiliser 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 deviennent plus élevés à mesure que le langage devient plus puissant). Chaque programmeur a probablement vu du code qu'une personne astucieuse a rendu marginalement plus court en utilisant des astuces de programmation douteuses.
Donc il n'y a pas de débat à ce sujet - du moins, pas de ma part. Les 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 s'il y avait un moyen excessivement compact de formuler quelque chose, il y aurait probablement aussi un moyen plus long. 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 un code qui serait 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 hors de ma tête, je m'intéresse à la question de savoir si une langue pourrait être trop concise. Y a-t-il des langues qui vous obligent à écrire du code d'une manière resserré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, et non pas simplement des programmes qui sont courts parce que les délimiteurs peuvent être omis et que tout a un nom d'un seul caractère.)