cours/.trash/check_text.md
Oscar Plaisant 66c143b7b3 update
2024-05-14 10:51:40 +02:00

17 KiB
Raw Blame History

Compromis entre expressivité et analysabilité

Plus un langage est expressif, plus il est complexe de l'analyser mathématiquement. Plus un langage est puissant expressivement au sens formel, plus il devient difficile (et impossible) de démontrer certains théorèmes sur ce formalisme [^29]. Cela est vrai également pour l'expressivité au sens pratique : "le fait d'éviter certaines techniques peut permettre de rendre plus aisée la démonstration de théorèmes sur la correction d'un programme -- ou simplement la compréhension de son fonctionnement -- sans limiter la généralité du langage de programmation." [@ParadigmeProgrammation2023].

Exemple de compromis : automates et grammaires

Les automates à pile et les machines de Turing forment un exemple de compromis entre analysabilité et expressivité formelle. Les automates à pile reconnaissent les langages non contextuels [^31]. Les machines de Turing reconnaissent les langages contextuels [^32]. Or, les langages non contextuels sont strictement inclus dans les langages récursivement énumérables [^33] . On peut donc conclure que les machines de Turing ont un pouvoir d'expression formel supérieur à celui des automates à pile.

Cependant, si on pose le problème de l'appartenance d'un mot à un langage donné. Ce problème est décidable pour tous les langages non contextuels [^34]. Pourtant, ce problème est indécidable pour les machines de Turing [^35].

On voit donc que les machines de Turing sont un formalisme plus expressif (certains concepts exprimés par des machines de Turing ne sont pas exprimables par des automates à pile) mais moins analysable (certains problèmes sont décidables sur les automates à pile, mais pas sur les machines de Turing).

Exemple de compromis : le non-déterminisme

Un langage de programmation est dit non déterministe lorsque son exécution ne dépend pas uniquement des spécifications du programme, c'est-à-dire que les spécifications laissent un choix lors de l'exécution du programme [^38]. Le non-déterminisme est donc problématique, puisqu'il peut amener à créer des programmes dont le résultat est inattendu ou incertain : il rend les programmes moins analysables. Ce pendant, certains paradigmes qui peuvent exprimer du non-déterminisme restent utiles pour modéliser certains problèmes (par exemple, la programmation concurrente, voir Remplissage du plan de L3#Programmation concurrente) [^39].

Une critique

C'est pourquoi certaines fonctionnalités, certains paradigmes, comme le non-déterminisme et les paradigmes qui l'impliquent, devraient être utilisés seulement si cela est nécessaire [^30].

Implications sur la diversité des paradigmes

Nous avons montré qu'il est nécessaire de faire des choix dans les concepts qu'implémente un paradigme, afin de faire un compromis entre analysabilité et expressivité. Cela donne une justification à l'existence de différents paradigmes : l'implémentation ou non de certains concepts à de réelles influences, non seulement sur l'expressivité des langages, mais également sur les propriétés de celui-ci (comme le déterminisme). Il est donc important de pouvoir choisir quels concepts utiliser selon les situations, afin de trouver le meilleur compromis entre expressivité et analysabilité.

Paradigmes dans l'apprentissage

Importance des paradigmes dans l'apprentissage

Les paradigmes de programmation jouent un rôle dans l'apprentissage d'un langage. Théoriquement les mêmes idées pourraient être représentées indépendamment du paradigme, mais le paradigme fournit un modèle de pensée, une approche, un cadre. Ces modèles de pensée sont utiles pour apprendre, car le fait d'avoir en tête des modèles, des idées générales sur la façon d'envisager un programme, permet d'apprendre plus aisément [^a]. Les paradigmes sont encore plus importants du point de vue de l'enseignant, qui doit identifier clairement les paradigmes qu'il enseigne pour pouvoir transmettre efficacement les concepts de la programmation [^b]. Le fait d'apprendre des paradigmes, des modèles mentaux, permet également de se détacher du langage de programmation particulier, et d'acquérir des connaissances générales, qui restent valides même lorsque l'on change de langage. Maîtriser un paradigme est utile pour tous les langages qui implémentent ce paradigme[^b].

Avantages de la diversité

On peut se demander s'il est intéressant d'apprendre plusieurs paradigmes au travers d'un ou plusieurs langages de programmation, si cette diversité des paradigmes est utile pour l'apprentissage.

Au fil du temps, à force de lire et travailler avec du code, nos compétences en programmation augment[^f] et l'on mémorise beaucoup de cas qui nous aideront à résoudre des problèmes plus intuitivement et à construire une bibliothèque mentale de modèles[^c] que n'aura pas un développeur débutant. Les sciences cognitives montrent que la différence principale entre un expert et un débutant n'est pas sa capacité à raisonner, mais plutôt à reconnaître des motifs qu'il a déjà vus [^g]. Cela montre l'importance de se construire une telle "bibliothèque mentale de modèles".

Conclusion sur les paradigmes pour l'apprentissage

Dans le contexte de l'apprentissage de la programmation, les paradigmes présentent plusieurs intérêts :

  • fournir des modèles mentaux à ceux qui apprennent, afin de mieux envisager la programmation
  • fournir des abstractions et des connaissances utiles indépendamment du langage.
  • fournir des abstractions, des motifs reconnaissables, qui finissent par former une "bibliothèque mentale de modèles"

Paradigmes pour la résolution de problèmes

Les paradigmes fournissent un cadre pour la pensée

La définition même de paradigme explique déjà en partie l'existence des différents paradigmes : un paradigme donne une façon d'envisager la programmation. Comme il existe de nombreux types de problèmes, de nombreuses situations à modéliser, il semble normal que de nombreux paradigmes existent pour donner les concepts et outils nécessaires afin d'implémenter efficacement des solutions à ces problèmes.

L'existence de nombreux paradigmes de programmation peut donc être justifiée par diversité des problèmes rencontrés : chaque paradigme permet de répondre à une classe de problèmes précis. Par exemple, la programmation concurrente (voir : Remplissage du plan de L3#Programmation concurrente) permet de modéliser des situations dans lesquelles deux événements indépendants évoluent en même temps et indépendamment.

L'efficacité peut ici avoir deux sens : l'efficacité pour l'humain, c'est-à-dire l'aisance avec laquelle le développeur pourra implémenter une solution à son problème; et l'efficacité lors de l'exécution (efficacité pour la machine), qui dépend du temps et de l'espace mémoire nécessaires à l'exécution du programme. La nécessité de faire des compromis entre expressivité et analysabilité décris plus haut (voir Remplissage du plan de L3#compromis entre expressivité et analysabilité) peut avoir pour conséquence la nécessité de faire des compromis entre efficacité pour l'humain et efficacité pour la machine. En effet, une plus grande efficacité pour l'humain peut être atteinte par une plus grande expressivité (théorique, mais surtout pratique); or, nous avons vu que cela pouvait mener à une moins grande analysabilité du langage, ce qui implique notamment que moins d'optimisation seront possibles lors de l'exécution d'un programme.

Avantages des langages multiparadigmes

Lors de la résolution de problèmes, il peut être utile d'avoir de nombreux modèles à disposition, afin de pouvoir choisir celui qui correspond le mieux au problème actuel. Si ces modèles sont directement implémentés dans notre langage de programmation (s'il supporte les bons paradigmes), la résolution de notre problème sera beaucoup plus aisée. "A language should ideally support many concepts in a well-factored way, so that the programmer can choose the right concepts whenever they are needed without being encumbered by the others." : Un langage devrait, dans l'idéal, intégrer de manière cohérente un grand nombre de paradigmes, pour permettre au développeur de choisir quels concepts il souhaite utiliser, sans être encombré par les autres [@royProgrammingParadigmsDummies p.10].

Certains langages ne nécessitent pas un grand pouvoir d'expression, car ils répondent à un besoin spécifique (voir Remplissage du plan de L3#Expressivité au sens formel). Cependant, la plupart des langages ont pour but de pouvoir résoudre une grande diversité de problèmes, et il est donc nécessaire qu'ils permettent de décrire et manipuler aisément un grand nombre de concepts.

créer un paradigme pour chaque type de problème

Le principe de l'extension créative (de l'anglais creative extension principle) est une méthode qui permet de créer de nouveaux paradigmes. Elle permet de trouver et d'organiser des concepts utiles à la programmation, pour réellement former un paradigme [^42].

L'extension créative part de la constatation qu'un problème nécessite des modifications envahissantes (des modifications dans l'ensemble des contextes du programme) pour être résolu. Il est alors nécessaire de comprendre cette difficulté (pourquoi cette modification devient-elle envahissante ?), et de trouver un concept plus général, plus fondamental, qui résout cette difficulté, c'est-à-dire qui permet d'éliminer ces modifications envahissantes pour retrouver un programme simple [^43].

Par exemple, si l'on cherche à modéliser plusieurs activités indépendantes dans un langage purement impératif, il faut implémenter soi-même plusieurs piles d'exécution, ainsi qu'un ordonnanceur... Les modifications impliquées par ce problème sont envahissantes. Cette complexité peut être évitée si notre langage implémente un concept (et donc un paradigme) : la concurrence (voir Remplissage du plan de L3#Programmation concurrente) [^44]. La même logique peut s'appliquer à la gestion d'erreurs dans un programme. Si l'on veut être capable de gérer les erreurs dans un programme, il faut ajouter des conditions dans le corps de chaque fonction, pour que chacune retourne le code d'erreur jusqu'à l'appel initial. Les modifications impliquées par ce problème sont envahissantes. Cette complexité peut être évitée si notre langage implémente un concept (et donc un paradigme) : les exceptions [^45].

Floyd, dans son papier "The Paradigms of Programming", explique une méthode similaire qui lui permet de créer de nouveaux paradigmes : lorsqu'il résout un problème complexe, il essaie d'extraire l'essence de sa solution, de la simplifier pour obtenir une solution aussi directe que possible. Il cherche ensuite une règle générale qui lui permettrait de résoudre des problèmes semblables. Cette règle, s'il la trouve, peut être le concept fondateur d'un nouveau paradigme [^46].

On peut donc voir chaque paradigme comme la réponse à un problème particulier, à une situation qui serait complexe à modéliser sans ce paradigme.

Il serait même pertinent, de ce point de vue, d'encourager la création de nouveaux paradigmes dès que l'on trouve des problèmes nouveaux qui sont complexes à résoudre avec les paradigmes existants.

Les paradigmes comme outil pour la pensée

Connaître un système de calcul ne permet pas d'immédiatement tout connaître sur son champ d'expressivité Notamment :

  • connaître un système de calcul ne permet pas (toujours) de connaître l'ensemble des problèmes décidables de ce système

Connaître un langage de programmation ne permet pas de savoir immédiatement comment résoudre tous les problèmes que l'on risque de rencontrer. Par exemple, la syntaxe des langages dérivés de LISP est très simple, et peut être apprise en peu de temps. Cependant, connaître la syntaxe complète et le fonctionnement d'un langage ne permettra pas de résoudre tous les problèmes : il est également nécessaire d'être capable de "faire le lien" entre un problème et son expression dans un langage de programmation. C'est ce lien que les paradigmes de programmation permettent de faire. Plus précisément, les paradigmes permettent de faire le lien entre un problème et une solution théorique, un modèle conceptuel qui permet ensuite d'implémenter une solution.

On peut notamment opposer les paradigmes et les méthodes. Une méthode permet de convertir en programme des problèmes déjà reconnus dans le cadre d'un paradigme, d'un écosystème. La méthode ne s'occupe pas de fournir une solution à un problème, ni un modèle pour ce problème, mais permet de convertir cette solution conceptuelle, ce modèle abstrait, en programme dans un langage particulier. Au contraire, les paradigmes explicitent plutôt quelle vision le développeur doit avoir, et quels concepts il peut utiliser pour construire son modèle du problème. Un paradigme donne donc un cadre pour modéliser un problème donné.

La figure (figref) montre les différentes étapes lorsque l'on est confronté à un problème :

  1. Résoudre le problème : trouver une idée de solution, et construire conceptuellement un modèle qui permet de résoudre notre problème. Ce sont les paradigmes qui nous permettent de faire cela, en donnant une vision sur ce qu'est un programme, et en fournissant des concepts utiles.
  2. Designer le programme : implémenter concrètement le programme dans un langage de programmation. Ce sont des méthodes, par exemple des design patterns, qui guide le développeur dans cette étape, en lui permettant de convertir des problèmes reconnus en programme dans un langage.
  3. Exécuter le programme : cette étape est réalisée par un compilateur ou un interpréteur.
  4. Interpréter les résultats : Pour que les données de sortie du programme deviennent véritablement de la connaissance, il faut leur attacher du sens et un contexte. Il faut pour cela qu'un être humain les interprète. Les statistiques fournissent notamment des outils pour interpréter des séries de données.

!paradigme_methode_execution_interprétation.excalidraw

Les paradigmes de programmations peuvent donc être vus comme un outil pour la pensée, qui permet de traduire un problème en une pré-solution, en un modèle conceptuel, en fournissant une vision sur la programmation, des modèles mentaux et des concepts utiles.

Conclusion

En fournissant un modèle pour penser les programmes informatiques, les paradigmes de programmation permettent à la fois d'améliorer l'apprentissage et la résolution de problèmes. En effet, l'abstraction fournie par les langages est utile à l'enseignement de la programmation (voir : Remplissage du plan de L3#Paradigmes dans l'apprentissages). Les divers concepts fournis par les différents paradigmes permettent également de modéliser au mieux les différents problèmes. Pour un programmeur, avoir à sa disposition un grand nombre de paradigmes permet alors de résoudre plus simplement une plus grande variété de problèmes (voir : Remplissage du plan de L3#Paradigmes pour la résolution de problèmes#Les paradigmes fournissent un cadre pour la pensée. On comprend notamment, dans ce contexte, l'intérêt particulier des langages multiparadigmes, qui laissent au développeur le choix d'utiliser ou non certains concepts selon ses besoins, mais sans avoir à changer de langage de programmation (voir : Remplissage du plan de L3#Paradigmes pour la résolution de problèmes#avantages des langages multi-paradigme). On peut par ailleurs voir les paradigmes comme résultant de la nécessité de résoudre certaines classes de problèmes. Un paradigme serait alors à créer pour chaque nouveau type de problème (voir : Remplissage du plan de L3#Paradigmes pour la résolution de problèmes#créer un paradigme pour chaque type de problème). Finalement, on peut comprendre le rôle général des paradigmes dans la résolution de problèmes : permettre la traduction des spécifications et enjeux d'un problème en un modèle conceptuel, en une idée de solution qui est entendable par le développeur (voir : Remplissage du plan de L3#Paradigmes pour la résolution de problèmes#Les paradigmes comme outil pour la pensée ).

Toutes ces raisons justifient l'existence des nombreux paradigmes de programmation, et encouragent même à en créer de nouveaux.

Annexes

contre la distinction entre les paradigmes

La distinction entre les différents paradigmes n'est pas toujours claire : beaucoup de langages sont Remplissage du plan de L3#les langages multi-paradigmes, et certains paradigmes peuvent être utilisés dans presque tous les langages (par exemple, la programmation structurée ref)

Certains auteurs considèrent que les paradigmes ne sont pas fondamentalement différents (voir : Remplissage du plan de L3#Au sens formel), mais plutôt que les paradigmes sont des courants de pensée, des traditions dans la programmation, rattachées à des communautés [^36].

Greg Michaelson critique la distinction des paradigmes, en expliquant que, lorsqu'on les analyse profondément, les paradigmes sont en fait proches entre eux [^37].