LA CONCISION EST PUISSANCE
OriginalMay 2002
"La quantité de signification compressée dans un petit espace par des 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 de remise 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 affirmation plutôt accablante à faire à propos d'un langage de programmation. Autant que je puisse en juger, concision = puissance. Si c'est le cas, alors en substituant, nous obtenons
L'objectif de Python est la régularité et la lisibilité, pas la puissance.
et cela ne semble pas être un compromis (si c'en est un) que vous voudriez 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 = puissance ? Cela me semble être une question importante, peut-être la plus importante pour quiconque s'intéresse à la conception des 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 puissance, 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 existent. Les ordinateurs seraient tout aussi heureux qu'on leur dise quoi 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, le principal objectif des langages de haut niveau est de rendre le code source plus petit.
Si un code source plus petit est le but des langages de haut niveau, et que la puissance de quelque chose est à quel point il atteint son but, alors la mesure de la puissance d'un langage de programmation est à quel point il rend vos programmes petits.
Inversement, un langage qui ne rend pas vos programmes petits fait 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 dans quel sens cependant ? 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 croie vraiment que c'est le véritable test de la longueur d'un programme. Différents langages ont différentes conventions sur combien vous devriez mettre sur une ligne ; en C, beaucoup de lignes n'ont rien d'autre qu'un ou deux délimiteurs.
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 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 chaque langage, donc ils n'affectent pas beaucoup les comparaisons.
Cette métrique a besoin d'être étoffé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 a. 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 en tant que guide dans la conception des 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 demander, que puis-je faire pour permettre aux programmeurs d'accomplir le plus de choses ? Et cela me semble identique à demander, comment puis-je concevoir un bon langage ?
(Incidemment, rien ne rend plus évident que le vieux dicton "tous les langages sont équivalents" est faux que de concevoir des langages. Lorsque vous concevez un nouveau langage, vous comparez constamment deux langages - le langage si je fais x, et si je ne le fais pas - pour décider lequel est meilleur. Si c'était vraiment une question sans signification, vous pourriez aussi bien lancer une pièce.)
Avoir pour objectif 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 aider en recherchant dans le code source des motifs répétés. Parmi d'autres langages, ceux ayant une réputation de concision seraient ceux à consulter pour de nouvelles idées : Forth, Joy, Icon.
Comparaison
La première personne à avoir écrit sur ces questions, autant que je sache, était Fred Brooks dans 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 ma vingtaine, cela m'a beaucoup surpris et semblait avoir d'énormes implications. Cela signifiait que (a) la seule façon d'écrire des logiciels 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 hacking. Au cours des années qui ont suivi, j'ai prêté une attention particulière à toute preuve que je pouvais 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'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, tendent à 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 des programmes qui prennent un mois à écrire. Et le seul véritable test, si vous croyez comme moi que le principal but d'un langage est d'être bon à penser (plutôt que simplement à dire à un ordinateur quoi faire une fois que vous y avez pensé), est quelles nouvelles choses vous pouvez écrire avec. 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 à 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 assez différents. En art, des médiums comme la broderie et la mosaïque fonctionnent bien si vous savez à l'avance ce que vous voulez créer, mais sont absolument mauvais 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 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 l'encre ou la peinture à l'huile. Et en effet, la façon dont les tapisseries et les mosaïques sont fabriquées en pratique est de faire d'abord une peinture, puis de la copier. (Le mot "carton" était à l'origine utilisé pour décrire une peinture destinée à cet usage).
Ce que cela signifie, c'est que nous ne sommes jamais susceptibles d'avoir des comparaisons précises du pouvoir relatif des langages de programmation. Nous aurons des comparaisons précises, mais pas exactes. En particulier, des études explicites dans le but de comparer des 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 réalisé une étude qui a conclu qu'Erlang était 4 à 10 fois plus concis que C++, et proportionnellement plus rapide pour développer des logiciels :
Les 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, indépendamment du langage (Erlang, PLEX, C, C++ ou Java) utilisé. Ce qui différencie les différents langages devient alors le volume de code source.
L'étude traite également explicitement d'un point qui n'était que 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 bogues. Cela devient une fin en soi, peut-être plus important 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. Quelle est la sensation de 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/de concevoir le langage qui se sent le mieux. 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 cela insupportablement restrictif de programmer dans des langages sans macros, tout comme quelqu'un habitué à la typage dynamique trouve cela 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 différents types.
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 le langage et qui accepteront n'importe quel emploi où elles peuvent utiliser ce langage, quel que soit le domaine d'application.
Restrictivité
Je pense que la plupart des hackers savent ce que cela signifie pour un langage de sembler restrictif. Que se passe-t-il lorsque vous ressentez cela ? Je pense que c'est le même sentiment que vous ressentez lorsque la rue que vous voulez prendre est bloquée, et que vous devez faire 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. 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 de la manière que vous aviez prévue, mais vous force à écrire le programme d'une autre manière qui était plus courte. Pour moi au moins, cela ne semblerait pas très restrictif. Ce serait comme si la rue que vous vouliez prendre était bloquée, et que le policier à l'intersection vous dirigeait vers un raccourci au lieu d'un détour. Super !
Je pense que la plupart (quatre-vingt-dix pour cent ?) du sentiment de restrictivité provient du fait d'être forcé de rendre le programme que vous écrivez dans le langage plus long que celui que vous avez dans votre tête. La restrictivité est principalement un manque de concision. Donc, lorsque un langage semble restrictif, ce que cela signifie (principalement) c'est qu'il n'est pas assez concis, et lorsque un langage n'est pas concis, il semblera restrictif.
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é, ou quel avantage, le cas échéant, un code qui est régulier et lisible a par rapport à 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 être prudents ici pour distinguer entre la lisibilité d'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 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 que vous entrez dans le pays de Greenspun). L'effort total de lecture du programme Basic sera sûrement plus grand.
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 que la puissance l'est, mais certainement la concision est un facteur (au sens mathématique ; voir l'équation ci-dessus) dans la lisibilité. Donc, il se peut même que ce ne soit pas significatif de dire que le but d'un langage est la lisibilité, pas la concision ; cela pourrait être comme dire que le but était la lisibilité, pas la lisibilité.
Ce que signifie la lisibilité par ligne, pour l'utilisateur rencontrant 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 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 en plusieurs fois : au lieu de les effrayer avec un prix élevé à l'avance, 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 l'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 précède les 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 mathématique peut être déconcertante. Cela pourrait prendre 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 cela peut sembler l'être. L'article mathématique 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'évoluer vers des notations concises), elles ne seraient pas plus faciles à lire, car le papier grandirait à la taille d'un livre.
Jusqu'à quel point ?
Un certain nombre de personnes ont rejeté l'idée que la concision = puissance. Je pense qu'il serait plus utile, au lieu de simplement argumenter qu'elles sont identiques ou ne le sont pas, de demander : jusqu'à quel point la concision = puissance ? Parce que clairement, la concision est une grande partie de ce pour quoi les langages de haut niveau existent. Si ce n'est pas tout ce pour quoi ils existent, alors à quoi d'autre servent-ils, et à quel point ces autres fonctions sont-elles importantes, relativement ?
Je ne propose pas cela juste pour rendre le débat plus civilisé. Je veux vraiment connaître la réponse. Quand, si jamais, 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 quiconque concevrait, ils seraient identiques, mais que si quelqu'un voulait concevoir un langage explicitement pour contredire cette hypothèse, il pourrait probablement le faire. Je ne suis même pas sûr de cela, en fait.
Langages, pas Programmes
Nous devrions être clairs que nous parlons de la concision des langages, pas 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 de nombreuses fois sa propre longueur pour être justifiée. Si écrire une macro complexe 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 encore être un mauvais choix, car 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 deviennent plus élevés à mesure que le langage devient plus puissant). Chaque programmeur a dû voir du code qu'une personne astucieuse a rendu marginalement plus court en utilisant des astuces de programmation douteuses.
Donc, il n'y a pas d'argument à 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 contraindre les programmeurs à écrire un 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 une manière excessivement compacte d'exprimer quelque chose, il y aurait probablement aussi une manière plus longue. Par exemple, si vous trouviez 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érez, écrire un code qui est 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 de tête, je suis intéressé par la question de savoir si un langage pourrait être trop concis. Existe-t-il des langages qui vous obligent à écrire du code d'une manière qui est rabougrie 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.)