De la beauté des langages de programmation…

Programming Babel - Hello World

Une source de discussion sans-fin parmi ceux qui programment est la comparaison des vertus relatives de langages de programmation. Depuis que la branche existe, les langages se sont succédés. Chaque nouvelle génération avec de nouvelles propriétés et de nouveaux concepts, qui devaient aider les gens à écrire leur programmes de manière plus élégante et expressive. Curieusement, malgré tous ces progrès, la qualité des programmes n’a pas tant augmenté que cela, et la norme dans les projets est d’utiliser des langages classiques. Une des raisons pour cela, à mon sens, c’est qu’avoir des langages élégants et expressif n’est pas la solution.

Le premier problème est celui de l’interfaçage. Un programme réel doit se connecter à d’autres composants, que ce soit pour le stockage, la transmission ou l’affichage de données. Ces composants sont généralement codés avec leur propre modèle, voire leur propre langage implicite. Les interfaces système d’Unix sont basées en C, Windows et Mac OS ont encore des traces de leur interfaces originales, qui étaient conçues en Pascal. Les données sont probablement contenues dans un fichier XML ou une base de données. Combien de langages ont une manière efficace et naturelle pour accéder à ces données ? L’interface se fait aussi dans l’autre sens, au niveau des outils pour interagir avec le programme : déboggueur, profileur, etc.

Le second problème est que programmer un projet tant soit peu complexe est une activité sociale. Une grande partie de la communication se fait avec d’autres êtres humains. La meilleure manière de communiquer avec un humain n’est pas d’utiliser un langage synthétique et élégant, mais une langue dans laquelle on n’a le plus de culture et compréhension commune avec ses interlocuteurs. Il y a une raison pour laquelle l’esperanto a été un échec, mieux vaut un mauvais langage commun, qu’une tour de babel de langages idéaux.

Même si théoriquement les langages informatiques sont tous équivalents, il faut du temps au cerveau humain pour qu’il s’habitue aux idiomes de tel ou tel langage. C’est une dépense d’énergie qui n’est ni productive, ni même agréable, vu qu’on ne peut plus se fier à son instinct. C’est probablement un bon exercice, mais ça ne fait pas avancer le boulot… De plus, même si les langages sont équivalents, la plupart des langages ne sont pas égaux, ils ont tous basés sur des assomptions et des styles quelque peu différents. Cela fait qu’il est possible de coder dans un langage A mais dans le style du langage B. Les règles de bon sens et de politesse ne sont, comment toujours, pas universelles.

Ce qui nous amène à la question de ce qu’un langage amélioré amène sur la table par rapport à ses prédécesseurs. Le problème c’est qu’en général, ils aident à écrire de manière plus concise et élégante du code simple (sauf les cas réellement ennuyeux qui sont nommés syntactic sugar et méprisés). L’hypothèse sous-jacente est que cela se répercutera sur un code plus complexe et que la qualité du code sera améliorée. Le problème c’est que ces langages ne proposent généralement rien pour les gros problèmes:

  • Accéder à de gros volumes de données efficacement
  • Exprimer des programmes en termes d’événements
  • Exprimer des tâches parallèles
  • Gérer les pannes

Le succès de Java s’explique principalement par le fait qu’il a résolu deux problèmes par rapport à C : le ramasse miette de la mémoire (garbage collection), et la gestion de threads, tout en gardant une grande partie de la syntaxe et des traditions de C.

6 thoughts on “De la beauté des langages de programmation…”

  1. Dans le cas de Python, il y a tout de même quelques bibliothèques qui traitent en tout cas de la parallélisation et de l’approche événementielle. C’est clair que ce ne sont pas des options natives au langage lui-même, mais elles sont déjà de bonne facture. Pour l’accès à de gros volumes de données, la question est aussi de savoir quelle former est-ce que ces volumes ont au départ et que voulons-nous en faire. Pour les pannes, j’en comprends instinctivement l’utilité, mais je ne suis pas certain de bien comprendre ce que tu entends par là et comment est-ce que ça devrait fonctionner.

  2. Tous les langages peuvent gérer le parallèlisme via des bibliothèques. Ce que j’aimerais revoir c’est des constructs “par” et “seq” comme en occam qui permette d’expliquer que deux instructions peuvent s’exécuter en parallèle. De même pour les fonctions asynchrones j’aimerais pouvoir spécifier la signature de la fonction, dire quels paramètre est en entrée, ou en sortie, et que le langage se débrouille pour me fournir un construct avec les données en sortie au moment ou le callback sera appelé. Une solution serait de créer des variables intelligentes, qui font le blocage / synchronisation des données, i.e. pouvoir écrire:

    a = fonction_asynchrone(“toto”);

    … code …

    b = a.resultat // bloque jusqu’à ce que fonction_asynchrone se soit terminée.

    Pour la tolérance aux pannes, cela revient à des question d’atomicité. I.e. si mon code est quelque chose comme

    atomic function c() {
    a();
    b();
    }

    Si b produit une exception, j’aimerais que les effets de a() soient annulés. Évidemment j’aimerais pouvoir spécifier si la fonciton est idempotente, constante, etc…

  3. “par” et “seq”, ok, je pense aussi et également depuis Occam que ce sont des choses qui manquent dans les langages actuels. Encore une fois je ramène à Python, puisque c’est quand même le langage que je connais le mieux actuellement, mais est-ce que les coroutines suffiraient pour exprimer ces idées ?

    Pour la fonction asynchrone, est-ce que un outil de gestion de contexte suffirait ? en Ruby et en Groovy on a les “closures” qui conviendraient à ce genre d’exercice, et en python c’est l’utilisation du mot “with” qui primerait, voire même tous les outils qui sont fournis par le framework Twisted Python. Le fait est aussi que l’approche de la parallélisation diffère assez d’un langage à l’autre.

    Dans le cas finalement de l’atomicité, ce qui manque après tout c’est à nouveau une gestion de contexte, avec un “commit” à la fin quand le traitement s’est achevé avec succès. Du transactionnel quoi… PJE (encore lui) développe un framework événementiel qui justement fonctionne par “impulsions”, où chaque impulsion est un “contexte” de travail annulable et réversible correspondant à une itération dans l’exécution d’un programme. Le défaut, c’est que dans le cas de Trellis, les objets qui peuvent être “rollbacké” doivent être des descendants des classes de composant de trellis et qu’il y a bien entendu des limites à ce qui est applicable.

    En somme, ce que tu demande semble exister en blibliothèques mais personne ne semble avoir eu l’idée de l’implémenter au niveau d’un langage. Mais est-ce que c’est souhaitable dans le fond ?

  4. Tu peux avoir des closures dans tous les langages, y compris C++, heck avec Boost tu peux avoir des lambdas. Pousser le problème dans des librairies ne règle pas le problème car ça fait que les librairies ne peuvent pas dépendre (trop) d’autres librairies.
    Cela faisait des plombes qu’il existait des librairies ajoutant un GC à C, et il existait depuis des années de librairies pour gérer des threads (même s’il s’agissait de pthreads), mais cela n’était pas naturel, donc en pratique les gens ne le faisaient que contraints et forcés.
    Repousser un problème dans une librairie est très satisfaisant intellectuellement, cela permet d’avoir une spec minimale, et ça fait très table de designer, avec presque rien de dessus, mais c’est pas pratique. Soit une librairie est standard, ou c’est un problème de plus: il faut l’apprendre, l’intégrer, la déployer. Un langage ne sert à rien en isolation et ne devrait donc pas être jugé ainsi. Les gens programment avec des plateformes, qui souvent impliquent un langage.

  5. Je me permet de m’insérer (avec retard ?) dans cette discussion.
    Ceci afin de compléter un peu le propos de Thias à sa réponse à edomaur.

    Je parlais justement ce midi avec des collègues des langages de programmation que je maitrisais. Vu que je peux en citer au moins une demi-douzaine, j’ai fini par leur expliquer que pour moi je distinguais en faite deux grandes familles de languages : les langages séquentiel/procéduraux et les langages objets.
    L’un d’entre nous à d’ailleurs rappeler qu’il suffisait à un langage de définir un mécanisme de callback et un mécanisme de structure, qualifié d'”à la C”, pour coder des objets.

    A mon sens, la seule différence existant entre les deux c’est une manière de percevoir les choses. Personnellement je déteste l’objet car sur un problème je suis incapable de concevoir d’instinct la structure de l’objet (donc sans ses métodes), par contre je n’ai aucun problème pour concevoir le séquencement des procédures (fonctions) par lequel doit passer le traitement pour arriver au résultat.
    Nous nous sommes donc rejoint sur ces notions même si nous n’avons pas les mêmes connaissances/usages, car au final tout langage informatique n’a qu’un seul but : décrire à un automate une liste d’actions à réaliser dans un ordre prédéterminer.
    Pour mémoire, rappelons qu’un processeur (coeur/séquenceur d’un ordinateur) c’est un automate déterministe (l’action A donnera toujours la réponse B) au niveau unitaire. C’est pourquoi pendant des années on représentait les mécanismes des ordinateurs sous forme de machine de Mealy ou de Moore.

    Maintenant on conçoit des mécanismes de plus en plus complexe, mais au final la machine n’interprètera qu’une seule instruction déterministe à la fois. Le problème revient donc à savoir si la traduction de l’expression de la pensée humaine peut être découpé automatiquement en atomes élémentaires de traitement déterministes ?

    Je finirais sur Python. Justement, je viens de m’y mettre pour raison professionnelle. J’ai donc décidé de prendre immédiatement un livre de référence sur le langage ; en effet on m’avait affirmé que Python c’était super parce qu’entre autre il n’était plus nécessaire de mettre des parenthèses (ou tout autre symbole séparateur d’atome) pour désigner une suite de commande.
    Ayant travaillé il y a déjà quelques temps sur des problématiques de conception de parser, j’ai voulu comprendre. Ma conclusion c’est que la solution proposée par Python (l’espace indentaire) est source/risque d’erreur à cause de la complexité combinatoire générée au niveau de la ‘traduction’ en atome élémentaire des instructions. Quand aux ”par” et aux ”seq” , je n’arrive pas à comprendre en quoi ils seraient manquant dans les langages actuels ?
    Pour faire simple, prenons le cas de seq. Il s’agit d’un mécanisme qui permet de manipuler une liste d’éléments de même nature (?). Si l’on prend du bon vieux C Ansi (1972), cela revient à avoir une fonction avec un pointeur sur la liste actuelle, et la référence de la donnée à ajouter/retirer (c’est de toute manière ce dont a besoin le cpu, généralement mis sur la pile, sauf bug). Ce que l’on ne décrit plus (mais si cela a été fait correctement au début cela ne sert à rien de changer) c’est le mécanisme pour trouver la donnée à retirer dans la liste.

    Comme on parler précédemment de bibliothèque, cela se faisait déjà en 1972 via une bibliothèque de fonction justement qui avait été ainsi vérifié intégralement (couverture fonctionnelle, de code,… à 100%) unitairement, car la combinatoire était moins importante. Ce n’est pas parceque l’on augment le nombre de bits que la logique/algorithme de découverte de la donnée change.
    Pour le programmeur, c’était donc déjà transparent.

  6. Pour simplifier un bloc “seq” se comporte comme un bloc dans un langage classique, i.e. SEQ {A, B, C} veut dire exécute A, puis B, puis C. Un bloc PAR {A, B, C } veut dire exécute A, B et C en parallèle, et quitte le bloc lorsque les trois instructions sont terminées.

Leave a Reply to edomaurCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.