dimanche 18 janvier 2009

Basic

Le langage Basic a été créé au milieu des années 60 pour permettre aux étudiants non-scientifiques de programmer un ordinateur. Autrement dit, un langage de programmation pour les non-programmeurs.

On peut dire que le langage a remplit sa mission, puisque (avec peut-être un petit coup de pouce de Bill)  il est devenu le langage d'apprentissage par excellence.

Le premier impératif de ce type de langage est d'être simple. Mais qu'est ce que la simplicité? La théorie a une définition interessante, car elle utilise justement la notion de programme pour quantifier la complexité d'une entité, d'un objet ou d'une donnée: c'est la taille minimale du programme qui produit la donnée ou une description de l'entité considérée. Seulement, on sait qu'un tout petit programme peut être long à s'executer et produire une donnée particulièrement complexe (on peut penser à un programme de décompression par exemple); ce qui amène à considérer le temps d'éxécution de ce programme, et à la distinction deux types de complexités: la complexité dite "aléatoire", qui est en quelque sorte liée à la quantité de données que porte l'objet, et la complexité logique, qui représente le degré d'élaboration de l'objet, c'est à dire en quelque sorte la quantité de travail qu'a nécessité sa construction.

La théorie parle d'un programme, donc d'un langage de programmation. Quel langage utilise-t-elle? C? Basic? Haskell? Celui de la machine de Turing? En fait, peu importe, il suffit d'utiliser toujours le même langage pour pouvoir faire des comparaisons. 

Puisque le source d'un programme est une donnée (textuelle), on peut appliquer cette définition aux programmes eux-mêmes. En particulier, on peut utiliser cette métrique pour un même programme écrit dans différents langages. On devrait mesurer ainsi la complexité relative de différents langages. Toutefois, cela revient plu ou moins à compter les lignes de code, autrement dit au fameux indice "SLOC" (Source Lines Of Code), dont on sait qu'il doit être utilisé avec prudence.

Compter les lignes que l'on doit écrire dans un langage pour obtenir un résultat simple comme afficher "Bonjour tout le monde" est quand même révélateur de certaines choses intéressantes. Cela sépare grosso modo les langages en deux catégories:

* les langages qui le font en une  ligne,

* les langages qui le font en plus de lignes.

La théorie et l'expérience concordent: les programmes de la seconde catégorie, qui consistent généralement en l'instruction "afficher 'bonjour'" entourée de lignes de code dont l'utilité est plus ou moins claire pour un non-initié, sont subjectivement et objectivement plus complexes.

Une autre façon de comparer les langages plus qualitative que quantitative est de dénombrer le nombre de concepts utilisés de manière plus ou moins directe dans ces programmes. Basic par exemple, n'en utilise que deux: le concept d'instruction, et le concept de chaine de caractère. Un langage à objets un tant soit peu rigoureux utilise en plus la notion de classe (ou de méthode). Un langage purement fonctionnel utilisera une monade ou imposera de définir une fonction.

Du point de vue du non-programmeur ou du débutant, un langage appartenant à la première catégorie comme Basic semble préférable, parce qu'il permet d'atteindre un objectif de manière simple. Pourquoi s'encombrer de lignes ou de concepts sans rapport direct avec le problème à résoudre? Pourquoi le langage se jette-t-il en travers du chemin?

Il est évident que les langages sont rarement conçu pour être gratuitement complexes. Les complications qu'ils introduisent ont pour but de se conformer à un paradigme, qu'on estime profitable à moyen ou long terme de suivre. Les concepts imposés qui peuvent paraître a priori superflus sont en général des investissements visant à améliorer certaines qualités internes du programme, telles que la fiabilité ou la maintenabilité sur le long terme. 

Mais surtout, ces "complications" permettent d'aller plus loin avec le langage. La théorie dit que tous les langages qui sont capables de simuler une machine de Turing sont tous capables de faire la même chose. Et donc, le langage de la machine de Turing est aussi capable de réaliser les mêmes choses qu'un langage évolué comme Haskell, Lisp ou SmallTalk. On réalise immédiatement ce que cela vaut en pratique: il serait extrêmement difficile par exemple, de trier un tableau de chaînes de caractères avec une machine de Turing. Le même inconvénient frappe plus ou moins les langages simples comme Basic: la réalisation des fonctionnalités devient de plus en plus difficile à mesure que ces fonctions deviennent évoluées. Les langages simples "décrochent", tandis que les langages plus complexes peuvent suivre l'évolution des besoins de l'utilisateur. La comparaison des exemples de programmes "Hello, world" avec ceux réalisés pour un programme un peu plus élaboré comme "99 bouteilles de bière" esquisse un peu les limites de la simplicité à tout prix.

Ces qualités ne sont pas sans interêt pour le non-programmeur. Obtenir un programme qui marche dans les cas prévus est le premier objectif; mais il appréciera sans doute aussi que celui-ci ne plante pas lamentablement en cas d'erreur de saisie. Et il appréciera aussi de pouvoir le faire évoluer. Par définition, un non-programmeur n'a aucune idée de la façon d'obtenir ces qualités - voire à l'extrême, il n'en a pas toujours conscience a  priori. Enfin, le non-programmeur devient, par la force des choses, un peu programmeur et est logiquement amené à faire des programmes plus ambitieux à mesure que ces compétences s'améliorent. Un "guidage" du langage peut être un élément de satisfaction vis-à-vis du langage ("ce langage est super car il sûr et évolutif"), même s'il recent ce guidage comme une contrainte ou un obstacle.

Un exemple emblématique de cela est l'instruction GOTO de Basic, qui est incomparablement plus séduisante que la programmation structurée (voire "stricturée"), mais qui, pour le moins, à conduit plus d'un programme "fait à la maison" à l'effacement du fait qu'il n'était plus possible de le modifier sans engendrer une nuée de bogues. Dans le même registre mais de manière un peu plus subtile, certains estiment que les exceptions, un moyen relativement intuitif et séduisant pour résoudre le problème de la gestion d'erreur, sont des GOTO (à peine) déguisés.

Est-ce à dire que les langages "compliqués" seraient en réalité plus adaptés aux non-programmeurs? Il suffit de prendre un exemple extrême comme le langage Haskell, ou d'une autre manière, Forth, pour se convaincre du contraire. L'un est un langage d'universitaire, l'autre un langage de "vrai" programmeur. Ces langages semblent empreinter des voies très détournées pour atteindre l'objectif de faire un programme qui execute une tâche déterminée. Certes, ces langages "à forte personnalité" excellent à atteindre les objectifs secondaires pour lesquels ils sont construits (forte optimisation et simplicité de conception pour l'un, formalisme rigoureux pour l'autres), et ses objectifs secondaires sont sensés aider à atteindre le but principal et/ou maximiser une ou plusieurs des qualités évoquées plus haut.

Mais le revers de la médaille est que cette volonté d'excellence dans ces domaines secondaires obligent à sacrifier certaines aides directes à l'accomplissement du projet principal (syntaxe inhabituelle ou cryptographique, "surcharge" de concepts ou de connaissances prérequises), alors que l'utilisateur n'a pas forcément besoin d'une extrême fiabilité, optimisation ou évolutivité. Cette excellence est ce qu'on appelle de la "surqualité", une qualité ou niveau de qualité qui n'était pas requis initialement, et qui est en fait néfaste car elle entraîne toujours sous une forme ou une autre un coût qui n'est pas justifié (du point de vue du client ou de l'utilisateur final).

Un compromis entre une simplicité qui rend le langage accessible au premier abord et une complexité qui le rend utilisable sur le long terme est donc à trouver. La formule de ce subtil équilibre a déjà été en grande partie par le langage Lisp:

  1.  les clôtures ("closures"), des entités qui peuvent être difficiles à comprendre mais qui s'avèrent très utiles. Elles permettent notamment de construire un système orienté-objet si on le souhaite (voir CLOS).
  2. un système de "macros" qui tient la route (celui de C/C++ est à côté une mauvaise plaisanterie), et qui permet de rendre plus expressif le langage quand les choses se compliquent.   

Mais il a un défaut majeur: le manque de lisibité de sa syntaxe préfixe et sa célèbre surabondance de parenthèses. La lisibilité d'un langage est une qualité qu'on sous-estime ou sacrifie trop souvent. Un langage lisible permet de communiquer clairement le modus operandi d'un programme. On utilise souvent du "pseudo-code" à cette fin plutôt que d'utiliser le langage d'implémentation, et c'est à mon sens à porter au passif dudit langage. Un langage clair devient un langage de communication aussi avec les autres utilisateurs, plus approprié que la langue naturelle qui parfois trop floue ou ambigüe (pour ne pas paraphraser: "mieux vaut un petit programme qu'un long discours"), ce qui facilite l'apprentissage et permet la coopération.

Aucun commentaire:

Enregistrer un commentaire