Programmation fonctionnelle

publicité
Programmation fonctionnelle
Aller à :Navigation,rechercher
La programmation fonctionnelle est un paradigme de programmation qui considère
le calcul en tant qu'évaluation de fonctions mathématiques et rejette le changement
d'état et la mutation des données. Elle souligne l'application des fonctions,
contrairement au modèle de programmation impérative qui met en avant les
changements d'état1.
Un langage fonctionnel est donc un langage de programmation dont la syntaxe et les
caractéristiques encouragent la programmation fonctionnelle. Alors que l'origine de la
programmation fonctionnelle peut être trouvée dans le lambda-calcul, le langage
fonctionnel le plus ancien est Lisp, créé en 1958 par McCarthy. Lisp a donné naissance
à des variantes telles que Scheme (1975) et Common Lisp (1984) 2, qui comme Lisp ne
sont pas ou peu typés. Des langages fonctionnels plus récents
tels ML (1973), Haskell (1987), OCaml, Erlang, Clean et Oz ou CDuce (2003) sont
fortement typés.
Sommaire
[masquer]
1 Machine d'états et effets secondaires
o
1.1 Programmation impérative
o
1.2 Programmation fonctionnelle
2 Transparence référentielle
3 Des fonctions passées en paramètre
4 Impact du paradigme fonctionnel
5 Références
Machine d'états et effets secondaires
[modifer]
Programmation impérative [modifer]
Article détaillé : programmation impérative.
En programmation impérative, on travaille sur le modèle des machines à états
(cf. Automate fni, machine de Turing et Architecture de von Neumann), avec une
mémoire centrale et des instructions qui modifent son état grâce à des affectations
successives. On peut représenter un programme par une machine d'états qui représente
les états successifs de la mémoire. Cela nécessite pour le programmeur de connaître à
tout instant un modèle exact de l'état de la mémoire que le programme modife.
Afn de réduire la diffculté que représente cette tâche, de nombreuses techniques
destinées à réduire le nombre de variables à gérer sont utilisées. On peut citer parmi
ces techniques les variables dites automatiques, dont la portée se limite à la procédure
dans laquelle elles ont été défnies et qui sont désallouées par le compilateur à la sortie
de la procédure, l'encapsulation des données, à l'origine de la programmation
structurée, et la programmation orientée objet.
Il arrive cependant que des procédures doivent mettre à jour certaines variables ou
zones de mémoire dans un but qui n'est pas directement lié à leur fonction, mais
uniquement afn que les données partagées restent dans un état prévu par le
programmeur. On regroupe ces modifcations « à distance » sous le terme générique
d'effets secondaires. Les effets secondaires, en programmation impérative, qui sont
plus la règle que l'exception, compliquent grandement la compréhension des
programmes et sont la source de nombreuses diffcultés et de bogues : en effet, si on
oublie de mettre à jour certaines données partagées, si l'ordre chronologique des
assignations par les différentes parties du logiciel est incorrect, ou si une zone de
mémoire a été désallouée au mauvais moment, le programme se retrouve dans un état
imprévu.
Programmation fonctionnelle [modifer]
La programmation fonctionnelle s'affranchit de façon radicale des effets secondaires
en interdisant toute opération d'affectation.
Le paradigme fonctionnel n'utilise pas de machine d'états pour décrire un programme,
mais un emboîtement de fonctions que l'on peut voir comme des « boîtes noires » que
l'on peut imbriquer les unes dans les autres. Chaque boîte possédant plusieurs
paramètres en entrée mais une seule sortie, elle ne peut sortir qu'une seule valeur
possible pour chaque tuple de valeurs présentées en entrée. Ainsi, les fonctions
n'introduisent pas d'effets de bord. Un programme est donc une application, au sens
mathématique, qui ne donne qu'un seul résultat pour chaque ensemble de valeurs en
entrée. Cette façon de penser, qui est très différente de la pensée habituelle en
programmation impérative est l'une des causes principales de la diffculté qu'ont les
programmeurs formés aux langages impératifs pour aborder la programmation
fonctionnelle. Cependant, elle ne pose généralement pas de diffcultés particulières aux
débutants qui n'ont jamais été exposés à des langages impératifs. Un avantage
important des fonctions sans effet de bord est la facilité que l'on a à les tester
unitairement. Par ailleurs, l'usage généralisé d'une gestion de mémoire automatique par
l'intermédiaire d'un ramasse-miettes (en anglaisgarbage collector) simplife la tâche du
programmeur.
En pratique, pour des raisons d'effcacité, et du fait que certains algorithmes
s'expriment aisément avec une machine d'états, certains langages fonctionnels
autorisent la programmation impérative en permettant de spécifer que certaines
variables sont assignables (oumutables selon la dénomination habituelle), et donc la
possibilité d'introduire localement des effets de bord. Ces langages sont regroupés sous
le nom de langages fonctionnels impurs.
Les langages dits purement fonctionnels n'autorisent pas la programmation impérative.
De fait, ils sont dénués d'effets de bord et protégés contre les problèmes que pose
l'exécution concurrente. On peut voir par exemple ce qui a été fait dans le cadre du
langage Erlang.
L'implémentation des langages fonctionnels fait un usage sophistiqué de la pile car
afn de s'affranchir de la nécessité de stocker des données temporaires dans des
tableaux ils font largement appel à la récursivité (fait d'inclure l'appel d'une fonction
dans sa propre défnition). La récursivité peut être rendue plus effcace à l'aide d'une
technique dénommée récursion terminale (en anglais tail-recursion), qui consiste à
accumuler les résultats intermédiaires dans une case mémoire de la pile et à la passer
en paramètre dans l'appel récursif. Ceci permet d'éviter d'empiler les appels récursifs
dans la pile en les remplaçant par une simple succession de sauts. Le code généré par
le compilateur est alors similaire à celui généré par une boucle en impératif. Certains
langages comme Scheme, OCaml et Anubisoptimisent automatiquement les appels
récursifs de cette manière.
Transparence référentielle
[modifer]
Les langages fonctionnels ont comme autre propriété la transparence référentielle. Ce
terme recouvre le principe simple selon lequel le résultat du programme ne change pas
si on remplace une expression par une expression de valeur égale. Ce principe est
violé dans le cas de procédures à effets de bord puisqu'une telle procédure, ne
dépendant pas uniquement de ses arguments d'entrée, ne se comporte pas forcément de
façon identique à deux instants donnés du programme. La transparence référentielle
est, comme nous allons le voir, une propriété extrêmement utile.
Prenons un exemple. En C, si n désigne une variable globale contenant un entier à
incrémenter (donc une case mémoire visible par tout le programme), et inc(k) une
fonction qui augmente la valeur de n de la quantité k :
int n = 2;
int inc(int k) { n = n + k; return n; } /* incrémentation par effet de bord
*/
f(inc(1) + inc(1));
Dans cet exemple, la fonction inc(i) ne retourne pas la même valeur lors des deux
appels : l'un des arguments de la fonction f vaudra 2 + 1 = 3 et l'autre 3 + 1 = 4. Il
s'avère donc impossible de remplacer inc(i) + inc(i) par 2 * inc(i) car la valeur
de inc(i) diffère à chaque appel. Ce comportement est fondamentalement différent de
celui d'une fonction mathématique. À l'échelle d'un programme important, cela signife
que le remplacement d'une fonction par une autre peut nécessiter des modifcations à
d'autres endroits du programme, et qu'il peut s'avérer nécessaire de retester l'intégralité
du système, car on n'est pas assuré qu'un tel remplacement n'a pas modifé son
comportement global. Au fur et à mesure que la complexité du système augmente, le
coût d'un changement s'accroît aussi. À l'inverse, la propriété de transparence
référentielle permet d'assurer que le remplacement d'une fonction par une autre
équivalente ne risque pas de modifer le comportement global du programme.
Autrement dit, elles sont mathématiquement égales. Cette propriété facilite la
maintenance logicielle. Elle permet aussi d'appliquer de façon automatique des
preuves de fonctionnement. Elle a enfn pour autre avantage de sensiblement réduire
l'importance de l'ordre d'exécution, celui-ci étant assuré par l'ordre d'appel des
fonctions ; un corollaire est que la parallélisation d'un programme fonctionnel peut être
réalisée de façon automatique.
Des fonctions passées en paramètre
[modifer]
Les langages fonctionnels emploient des types et des structures de données de haut
niveau comme les listes extensibles. Il est ainsi généralement possible de réaliser
facilement des opérations comme la concaténation de listes, ou l'application d'une
fonction à une liste - le parcours de la liste se faisant de façon récursive -, en une seule
ligne de code.
Un mécanisme puissant des langages fonctionnels est l'usage des fonctions d'ordre
supérieur. Une fonction est dite d'ordre supérieur lorsqu'elle peut prendre des
fonctions comme argument (aussi appelées callback) et/ou retourner une fonction
comme résultat. On dit aussi que les fonctions sont des objets de première classe, ce
qui signife qu'elles sont manipulables aussi simplement que les types de base. Elles
correspondent, en mathématiques, aux fonctionnelles. Les opérations de dérivation et
d'intégration en sont deux exemples simples. Les fonctions d'ordre supérieur ont été
étudiées par Alonzo Church et Stephen Kleene dans les années 1930, à partir du
formalisme du lambda-calcul qui a infuencé la conception de plusieurs langages
fonctionnels, notamment celle d'Haskell. Toutefois la Théorie des Catégories
Cartésiennes Fermées caractérise les fonctions d'une façon plus moderne à l'aide de la
notion de problème universel.
Impact du paradigme fonctionnel
[modifer]
La programmation fonctionnelle reste peu utilisée en dehors du milieu universitaire et
des hobbyistes. Dans le milieu industriel, les langages Erlang (développé
par Ericsson pour des besoins de programmation concurrentielle et des impératifs de
robustesse), Common Lisp et Scheme sont utilisés. Mais le développement des
langages fonctionnels est limité par le défcit en outils et en bibliothèques de qualité
commerciale, et surtout par le manque de programmeurs formés. Notons aussi le
langage F# développé par Microsoft Research.
Enfn, les langages fonctionnels souffrent encore d'une réputation de lenteur
aujourd'hui complètement injustifée : certains compilateursScheme, comme les
compilateurs Stalin ou Bigloo, les compilateurs pour Common Lisp, les langages dans
la lignée de ML, tels queObjective Caml ou encore Haskell produisent des exécutables
dont les performances moyennes sont comparables à ceux produits par les
compilateurs C ou C++. Un code sans effet de bord étant beaucoup plus robuste et plus
facile à maintenir, on le préférera à un programme dont la gestion de la mémoire et la
prédiction du comportement seront diffciles à maîtriser, quitte à perdre un peu en
effcacité.
En dehors des universités et de secteurs industriels spécifques, XSLT est peut être un
vecteur de popularisation du paradigme fonctionnel. Dédié au traitement XML, il
permet à un programmeur de conserver ses langages impératifs ou objets, tout en
découvrant une autre logique de programmation.
Références
[modifer]
1. ↑ Paul Hudak, « Conception, evolution, and application of functional programming
languages », dans ACM Computing Surveys, vol. 21, no 3, September 1989, p. 359-411 [texte
intégral [archive]]
2. ↑ auxquels il faut ajouter XSLT et Anubis.
Téléchargement