Loading...

LA CONCISION EST UN POUVOIR

Original

Mai 2002

« La quantité de sens comprimé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 à leur aide. »

  • Charles Babbage, cité dans la conférence d'Iverson pour le prix Turing

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.

L'objectif de Python est la régularité et la lisibilité, pas la concision.

A première vue, cela semble être une affirmation plutôt accablante à propos d'un langage de programmation. D'après ce que je peux en dire, concision = puissance. Si c'est le cas, alors en remplaçant, on obtient

L'objectif de Python est la régularité et la lisibilité, pas la puissance.

et cela ne semble pas être un compromis (si c'est un compromis) que vous voudriez faire. Il 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 est-elle synonyme de puissance ? Cela me semble être une question importante, peut-être la plus importante pour quiconque s'intéresse à la conception du langage, et une question qu'il serait utile d'aborder directement. Je ne suis pas encore sûr que la réponse soit un simple oui, mais cela me semble une bonne hypothèse pour commencer.

Hypothèse

Mon hypothèse est que la concision est un pouvoir, ou est suffisamment proche pour que, sauf dans les exemples pathologiques, vous puissiez les traiter comme identiques.

Il me semble que la concision est le but des langages de programmation. Les ordinateurs seraient tout aussi heureux qu'on leur dise ce qu'ils doivent faire directement en langage machine. Je pense que la principale raison pour laquelle nous nous donnons la peine de développer des langages de haut niveau est d'avoir un effet de levier, afin de pouvoir dire (et surtout 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 rendre le code source plus petit.

Si le but des langages de haut niveau est de réduire la taille du code source et que la puissance d'une chose est la façon dont elle atteint son objectif, alors la mesure de la puissance d'un langage de programmation est la taille qu'il réduit pour vos programmes.

À l’inverse, un langage qui ne rend pas vos programmes petits fait du mauvais travail dans ce que les langages de programmation sont censés faire, comme un couteau qui ne coupe pas bien ou une impression illisible.

Métrique

Mais dans quel sens ? La mesure la plus courante de la taille d'un code est le nombre de lignes de code. Mais je pense que cette mesure est la plus courante car 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. Les différents langages ont des conventions différentes sur la longueur à mettre sur une ligne ; en C, de nombreuses lignes ne contiennent rien d'autre qu'un ou deux délimiteurs.

Un autre test simple 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 d'une directive de format, est un élément ; un nouveau bloc est un élément. Il existe des cas limites (est-ce que -5 représente 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 mesure doit être précisée et peut 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 d'un programme. 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 mesure 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 pour la conception de langages. La comparaison la plus utile entre langages est celle entre deux variantes potentielles du même langage. Que puis-je faire dans le langage pour raccourcir les programmes ?

Si la charge conceptuelle d'un programme est proportionnelle à sa complexité et qu'un programmeur donné peut tolérer une charge conceptuelle fixe, cela revient à se demander ce que je peux faire pour permettre aux programmeurs d'être plus productifs. Et cela me semble identique à se demander comment concevoir un bon langage.

(D'ailleurs, rien ne rend plus évident que le vieux dicton selon lequel « 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 le langage si je ne le faisais pas – pour décider lequel est le meilleur. Si cette question était vraiment dénuée de sens, vous pourriez aussi bien lancer une pièce de monnaie.)

La concision semble être un bon moyen de trouver de nouvelles idées. Si vous parvenez à raccourcir de nombreux programmes différents, ce n'est probablement pas une coïncidence : vous avez probablement découvert une nouvelle abstraction utile. Vous pourriez même être capable d'écrire un programme pour vous aider en recherchant dans le code source des modèles répétés. Parmi les autres langages, ceux qui ont la réputation d'être concis sont ceux vers lesquels se tourner pour trouver 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 le Mythical Man Month . Il écrivait que les programmeurs semblaient générer à peu près la même quantité de code par jour, quel que soit le langage. Quand j'ai lu cela pour la première fois, au début de ma vingtaine, cela m'a beaucoup surpris et cela m'a semblé 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 piratage informatique. Au cours des années qui ont suivi, j'ai prêté une attention particulière à toutes les preuves que j'ai pu recueillir sur la question, des études formelles aux anecdotes sur des projets individuels. Je n'ai rien vu qui puisse le contredire.

Je n'ai pas encore vu de preuves qui me semblent concluantes, et je ne m'attends pas à en voir. Des études comme celle de Lutz Prechelt sur la comparaison des langages de programmation, bien que générant le type de résultats que j'attendais, ont tendance à utiliser des problèmes trop courts pour constituer des tests significatifs. Un meilleur test d'un langage est celui qui se produit dans des programmes dont l'écriture prend un mois. Et le seul véritable test, si vous croyez comme moi que le but principal d'un langage est d'être agréable à utiliser (plutôt que de simplement dire à un ordinateur ce qu'il doit faire une fois que vous y avez pensé), est de savoir quelles nouvelles choses vous pouvez y écrire. Ainsi, toute comparaison de langages où vous devez respecter une spécification prédéfinie teste légèrement la mauvaise chose.

Le véritable critère de la qualité d'une langue est la capacité à découvrir et à résoudre de nouveaux problèmes, et non la capacité à l'utiliser pour résoudre un problème déjà formulé par quelqu'un d'autre. Ces deux critères sont très différents. En art, des techniques comme la broderie et la mosaïque fonctionnent bien si vous savez à l'avance ce que vous voulez faire, mais sont absolument nulles si vous ne le savez pas. Lorsque vous voulez découvrir l'image au fur et à mesure que vous la créez - comme c'est le cas pour tout ce qui est aussi complexe que l'image d'une personne, par exemple -, vous devez utiliser un médium plus fluide comme le crayon, le lavis d'encre ou la peinture à l'huile. En fait, la façon dont les tapisseries et les mosaïques sont réalisées dans la pratique consiste à faire d'abord une peinture, puis à la copier. (Le mot « dessin animé » était à l'origine utilisé pour décrire une peinture destinée à cet usage).

Cela signifie que nous ne pourrons probablement jamais comparer avec précision la puissance relative des langages de programmation. Nous aurons des comparaisons précises, mais pas exactes. En particulier, les études explicites visant à 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 que 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é ligne/heure similaire, y compris toutes les phases de développement logiciel, indépendamment du langage utilisé (Erlang, PLEX, C, C++ ou Java). Ce qui différencie les différents langages est alors le volume du code source.

L'étude traite aussi 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, peut-être plus importante que la productivité du programmeur, dans des applications comme les commutateurs réseau.

Le test du goût

En fin de compte, je pense qu'il faut se fier à son instinct. Que ressent-on lorsqu'on programme dans un langage ? Je pense que la meilleure façon de trouver (ou de concevoir) le meilleur langage est de devenir hypersensible à la façon dont un langage vous permet de réfléchir, puis de choisir/concevoir le langage qui vous convient le mieux. Si une fonctionnalité du langage est gênante ou restrictive, ne vous inquiétez pas, vous le saurez.

Cette hypersensibilité aura un prix. Vous vous rendrez compte que vous ne supportez pas de programmer dans des langages peu pratiques. Je trouve insupportablement restrictif de programmer dans des langages sans macros, tout comme quelqu'un habitué au typage dynamique trouve insupportablement restrictif de devoir revenir à la programmation dans un langage où il faut déclarer le type de chaque variable et ne peut pas créer 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 le langage et qui accepteraient n'importe quel emploi dans lequel ils auraient l'occasion d'utiliser ce langage, quel que soit le domaine d'application.

Caractère restrictif

Je pense que la plupart des hackers savent ce que signifie pour un langage de se sentir 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 emprunter est bloquée et que vous devez faire un long détour pour arriver à votre destination. Vous avez quelque chose à 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 que vous vouliez écrire un programme et que le langage ne vous permette pas de l'exprimer comme vous l'aviez prévu, mais vous oblige à écrire le programme d'une autre manière plus courte. Pour moi du moins, cela ne me 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 indiquait un raccourci au lieu d'un détour. Super !

Je pense que la plupart (90 % ?) du sentiment de restriction vient du fait que vous êtes obligé d'écrire un programme plus long que celui que vous avez en tête. La restriction est principalement due à un manque de concision. Ainsi, lorsqu'un langage semble restrictif, cela signifie (principalement) qu'il n'est pas assez succinct, et lorsqu'un langage n'est pas succinct, il semble restrictif.

Lisibilité

La citation avec laquelle j'ai commencé mentionne deux autres qualités, la régularité et la lisibilité. Je ne sais pas exactement ce qu'est la régularité, ni quel avantage, le cas échéant, un code régulier et lisible présente par rapport à un code simplement lisible. Mais je pense savoir ce que l'on entend par lisibilité, et je pense que cela est également lié à la concision.

Il faut ici faire attention à bien distinguer la lisibilité d'une ligne de code individuelle de la lisibilité du programme dans son ensemble. C'est la seconde qui compte. Je suis d'accord qu'une ligne de code Basic est probablement plus lisible qu'une ligne de code Lisp. Mais un programme écrit en Basic aura plus de lignes que le même programme écrit en Lisp (surtout une fois que vous aurez franchi le pas vers Greenspunland). 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 que je le suis que la puissance l'est, mais la concision est certainement un facteur (au sens mathématique du terme ; voir l'équation ci-dessus) de lisibilité. Il n'est donc peut-être même pas pertinent de dire que le but d'une langue est la lisibilité et non la concision ; cela pourrait être comme dire que le but est la lisibilité et non la lisibilité.

Pour l'utilisateur qui découvre le langage pour la première fois, la lisibilité par ligne signifie que le code source ne semblera pas menaçant . La lisibilité par ligne peut donc être une bonne décision marketing, même si elle est une mauvaise décision de conception. Elle est similaire à la technique très efficace qui consiste à laisser les gens payer en plusieurs fois : au lieu de les effrayer avec un prix initial élevé, vous leur annoncez le faible paiement mensuel. Les plans de paiement échelonné sont toutefois une perte nette pour l'acheteur, comme la simple lisibilité par ligne l'est probablement pour le programmeur. L'acheteur va effectuer beaucoup de ces paiements très faibles, et le programmeur va lire beaucoup de ces lignes lisibles individuellement.

Ce compromis est antérieur aux 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 peut vous falloir une demi-heure pour lire une seule page. Et pourtant, je suis presque sûr que la notation n'est pas le problème, même si on peut avoir l'impression que c'est le cas. 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 succinctes), elles ne seraient pas plus faciles à lire, car l'article atteindrait la taille d'un livre.

Dans quelle mesure ?

Un certain nombre de personnes ont rejeté l'idée selon laquelle la concision = la puissance. Je pense qu'il serait plus utile, au lieu de simplement argumenter sur le fait que ces deux choses sont identiques ou non, de se demander : dans quelle mesure la concision = la puissance ? Car il est clair que la concision est une grande partie de ce à quoi servent les langages de haut niveau. Si ce n'est pas leur seule utilité, 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 connaître la réponse. Quand, si jamais, un langage est-il trop concis pour son propre bien ?

L'hypothèse sur laquelle je suis parti était que, sauf dans les cas 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, 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 n'en suis même pas sûr, en fait.

Des langues, pas des programmes

Il faut bien comprendre que nous parlons ici de la concision des langages et non de programmes individuels. Il est tout à fait possible que des programmes individuels soient rédigés 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 compliquée pouvait vous faire économiser dix lignes de code à chaque fois que vous l'utilisez, et que la macro elle-même représente dix lignes de code, alors vous obtenez une économie nette de lignes si vous l'utilisez plus d'une fois. Mais cela pourrait toujours être une mauvaise décision, car les définitions de macro sont plus difficiles à lire que le code ordinaire. Vous devrez peut-être utiliser la macro dix ou vingt fois avant qu'elle n'apporte une nette amélioration de la lisibilité.

Je suis sûr que chaque langage présente de tels compromis (même si je soupçonne que les enjeux augmentent à mesure que le langage devient plus puissant). Chaque programmeur a dû voir du code qu'une personne intelligente a rendu légèrement plus court en utilisant des astuces de programmation douteuses.

Il n'y a donc aucun doute là-dessus, du moins pas de ma part. Les programmes individuels peuvent certainement être trop succincts pour leur propre bien. La question est de savoir si un langage peut l'être. Un langage peut-il obliger les programmeurs à écrire du code court (en éléments) au détriment de la lisibilité globale ?

L'une des raisons pour lesquelles il est difficile d'imaginer un langage trop succinct est que s'il existait une manière excessivement compacte de formuler quelque chose, il existerait probablement aussi une manière plus longue. Par exemple, si vous pensez que les programmes Lisp utilisant beaucoup de macros ou de fonctions d'ordre supérieur sont trop denses, vous pouvez, si vous le préférez, écrire du code isomorphe au Pascal. Si vous ne voulez pas exprimer la factorielle dans 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)))))

Même si je ne parviens pas à trouver d'exemples, je m'intéresse à la question de savoir si un langage peut être trop succinct. Existe-t-il des langages qui vous obligent à écrire du code de manière brouillonne 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 très denses selon la métrique des « éléments » esquissée ci-dessus, et pas simplement des programmes courts car les délimiteurs peuvent être omis et tout a un nom à un seul caractère.)