Concepts des langages de programmation Programmation fonctionnelle Plan Introduction Principes de la programmation fonctionnelle Concepts de la programmation fonctionnelle Applications des langages fonctionnels Comparaison des langages fonctionnels et impératifs 2 Introduction La conception des langages de programmation impératifs est principalement basée sur l'architecture de von Neumann (à base de registres) Conception liée à l'architecture des machines La conception des langages fonctionnels est basée sur les fonctions mathématiques Une base théorique solide qui est proche de l'utilisateur mais non liée à l'architecture des machines sur lesquelles les programmes vont s'exécuter Tout programme dans les langages fonctionnels est vu comme une fonction mathématique 3 Principes de la programmation fonctionnelle Les programmes doivent se comporter comme des fonctions mathématiques Étant donné un ensemble d’entrée, la fonction devrait retourner toujours les mêmes valeurs Ce qui signifie que les fonctions ne sauvegardent pas d’état entre deux appels 4 Fonctions sans états Convient parfaitement au monde de la programmation concurrente Concepts de la programmation fonctionnelle Fonctions mathématiques Fonctions lambda Fonctions d'ordre supérieur Composition de fonctions Appliquer-à-tout Transparence référentielle Interfaçage Évaluation paresseuse 5 Fonctions mathématiques Une fonction mathématique est une mise en correspondance des éléments d'un ensemble, appelé domaine, aux éléments d'un autre ensemble appelé image ou co-domaine Le domaine de la fonction correspond aux données fournies au programme ("l'entrée") L'image de la fonction correspond aux données produites par le programme ("la sortie") Exemple Fonction : somme(x,y) = x+y somme(2,3) = 5 6 2 et 3 constituent l'entrée de la fonction somme (paramètres d'entrée) et 5 constitue la sortie (résultat) Les expressions Lambda Une expression lambda spécifie les paramètres et la mise en correspondance d'une fonction dans la forme ci-dessous : λ(x) x * x * x Est équivalent à la déclaration : cube(x) = x * x * x 7 Les expressions Lambda (suite) Les expressions Lambda décrivent des fonctions anonymes (fonctions sans noms) F(x) = x*x+ 10 => fonction nommée λ(x) x*x+ 10 => fonction anonyme Les expressions Lambda sont appliquées aux paramètres placés après l'expression Exemple : 8 (λ(x) x * x * x)(2) Sera évaluée à 8 (2*2*2) Fonction d'ordre supérieur Une fonction d'ordre supérieur, ou forme fonctionnelle, est une fonction qui accepte en paramètres des fonctions ou retourne une fonction en résultat ou bien les deux 9 Composition de fonctions La composition de fonction, est une fonction d'ordre supérieur qui accepte deux fonctions en paramètres et retourne une fonction en sortie Forme : Ce qui signifie : h ≡ f ° g h(x) ≡ f ( g ( x)) Pour f(x) ≡ x + 2 et g(x) ≡ 3 * x, h ≡ f ° g donne : (3 * x)+ 2 10 Appliquer-à-tout (Apply-to-all) Une fonction d'ordre supérieur qui accepte une seule fonction en tant que paramètre et retourne comme résultat une liste de valeurs obtenues de l'application de cette fonction à chaque élément d'une liste de paramètres Forme : α Exemple Pour : h(x) ≡ x * x, 11 α( h, (2, 3, 4)) retournera : (4, 9, 16) Transparence référentielle Dans la programmation fonctionnelle, l'évaluation d'une fonction produit toujours les mêmes résultats pour les mêmes paramètres Résultat reste le même lorsqu'on change une expression par une autre expression de valeur égale Ce principe est entre autres, pratique pour : Faciliter la transformation de programmes Exemple : 12 Les rendre plus lisibles, plus simple, améliorer les performance ou la compilation Une règle qui dit : x + x = 2 * x permet de remplacer f(y) + f(y) par 2 * f(y) Transparence référentielle (suite) La programmation impérative viole la transparence référentielle à cause des effets de bords de quelques fonctionnalités (exemple : affectations et changement du contenu de fichiers) Exemple 13 n=1 def f(x) n +=1 return n * x end f(x) + f(x) est différent de 2 * f(x) Conséquences de la transparence référentielle La valeur d’une expression dépend seulement de la valeur de ses sous-expressions (exemple : la valeur de E1 + E2 dépend seulement de la valeur de E1 et de E2, et la valeur de E2 n’est pas influencée par E1, et vice versa) L’ordre d’exécution a beaucoup moins d’importance qu’en programmation impérative (E1 peut être évaluée avant, après ou pendant l’évaluation de E2) 14 Ce qui rend la programmation fonctionnelle bien adaptée à la programmation concurrente Interfaçage (Currying) Adaptation d'une fonction à un usage particulier Exemple Calcul de la puissance : Adaptation de la fonction puissance pour le calcul du carré : 15 def puissance(i, j) I ** j end def carre(i) puissance(I,2) end Évaluation paresseuse (Lazy evaluation) Problème : Solution : Dans la programmation impérative, une fonction est exécutée dés qu'elle est appelée La valeur de la fonction peut ne pas être utilisée L'exécution de la fonction est donc inutile Les langages fonctionnels proposent la possibilité d'utiliser une évaluation (exécution) paresseuse des fonctions Principe de l'évaluation paresseuse : n'évaluer une fonction qu'au moment où sa valeur sera utilisée 16 Évaluation paresseuse (suite) Exemple en Scala : Fonction Fibonacci : Affectation de la (valeur de la) fonction à une variable Console.println(a) // => Lazy(?) Utilisation de a : 17 var a = lazy({ fib(42) }) Tentative d'affichage de a (la valeur n'est pas calculée) : def fib(n: int): int = n match { case 0 => 0 case 1 => 1 case _ => fib(n-1)+fib(n-2) } a+1 // => 267914297 Application des langages fonctionnels Les langages fonctionnels sont utilisés pour 18 La représentation des connaissances L'apprentissage machine Traitement des langues naturels Modélisation de la parole et de la vision Programmation concurrente … Programmation fonctionnelle vs. programmation impérative Langages impératifs Exécution efficace La concurrence est la responsabilité du programmeur Quelques langages impératifs inclus des fonctionnalités des langages fonctionnels (exemple : Ruby,…) Langages fonctionnels Exécution pas très efficace Facilité de débogage : Les programmes peuvent facilement être rendus concurrents : 19 une erreur peut être reproduite facilement car le bug ne dépend pas du chemin d'exécution (ne dépend pas du code précédent le code ayant généré l'erreur) grâce à la non sauvegarde d'état dans les fonctions : l'exécution d'une fonction par un thread x ou un thread y n'a pas d'importance et donnera toujours le même résultat Quelques langages fonctionnels inclus des fonctionnalités des langages impératifs (ex. LISP, Scala, …) Résumé Les langages de programmation fonctionnelle utilise des fonctions pour contrôler l'exécution des programmes à la place des variables et des affectations qui sont utilisées dans les langages de programmation impérative Les langages de programmation fonctionnels pures ont quelques avantages sur les langages de programmation impératifs. Cependant, ils sont moins efficaces sur les machines existantes dont l'architecture est plus adaptée aux langages de programmation impératifs 20