La programmation fonctionnelle n’est pas un jouet pour académiciens


Bonjour,

Cela fait maintenant un moment que j’apprends et pratique la programmation fonctionnelle, aussi bien sur le point de vue théorique en m’intéressant à ses relations avec la théorie des catégories (mathématiques) entre autres, que sur le point de vue pratique, c’est à dire que j’ai mené quelques projets (de petite envergure… oui, c’est proportionnel au temps que je peux passer dessus) à bien.

Quand on dit « langage fonctionnel » ou « programmation fonctionnelle », les gens s’imaginent (pour ceux qui ne connaissent pas) des programmes avec tout un tas de fonctions, c’est tout. Ce n’est pas ça la programmation fonctionnelle. Enfin biensûr il y a des fonctions, mais la programmation fonctionnelle ne s’arrête pas là.

Déjà, la programmation fonctionnelle, c’est une toute autre façon de concevoir des programmes. Détailler toutes les différences avec la programmation impérative serait pire que les 12 travaux d’Hercules et c’est pourquoi je ne le ferai pas. Je vais toutefois vous parler des points qui font que je suis désormais adepte de ce style de programmation.


Tout d’abord, étant assez orienté mathématiques, il y a une première chose qui me plaît. Dans les langages de programmation fonctionnelle (du moins la plupart), on définit des types, des opérations sur ces types, le tout très simplement et de manière on ne peut plus cohérente.

Ensuite, pour les langages fonctionnels compilés et typés statiquement, on est généralement accompagné d’un compilateur très strict, qui tentera d’éliminer un maximum d’incohérences (comprendre « erreurs ») en nous envoyant des erreurs très précises en rapport avec le fait que nous n’avons pas bien typé des parties de notre programme. De plus, les 3 langages majeurs pour une utilisation « real world », Haskell, OCaml et F#, ont vu leur compilateur se doter de l’inférence de type ; il s’agit de la capacité qu’a un compilateur a donner lui-même un type à vos valeurs dans le programme (fonctions, expressions, …). Entre ceci et la vérification des types, la compilation possède une part de travail autour des types en action dans votre code.

De plus, en programmation fonctionnelle, il s’agit de ne pas avoir ce que l’on appelle des effets de bords ; on parle de programmation fonctionnelle pure ; dans un tel cadre, une fonction à qui l’on donne les mêmes entrées renverra toujours la même sortie. Les programmeurs fonctionnels font en sorte d’isoler les parties pour lesquelles ce n’est pas vrai (lecture d’un fichier, intéraction réseau, …) : monades en Haskell, on laisse la possibilité de faire de l’impératif pour OCaml et F#, …

Continuons sur la curryfication. Cette dernière consiste à faire par exemple d’une fonction de 2 arguments 2 fonctions à 1 argument, qui pourront s’appliquer partiellement. Si l’on définit une fonction « add » pour ajouter 2 nombres x et y, alors on pourra appliquer add à son premier argument, x, en lui donnant par exemple la valeur 12, pour obtenir une fonction à UN SEUL argument qui additionnera son argument à 12.

Les fonctions sont capitales en programmation fonctionnelle. Contrairement à dans un certain nombre de langages (beaucoup), les fonctions peuvent être stockées, passées en argument, construites à la volée, retournées par d’autres fonctions, etc. Elles deviennent des valeurs de première classe.

A propos du passage en argument, une chose très importante en programmation fonctionnelle est la possibilité d’abstraire une partie des algorithmes et d’en confier la responsabilité à une fonction donnée en argument par exemple. Une fonction qui prend une autre fonction en argument est appelée « fonction d’ordre supérieur ». Cela permet de factoriser un maximum de code tout en laissant la variabilité possible grâce au passage de fonction en argument. Le compilateur fera les vérifications nécessaires pour voir si les types concordent, ne vous inquiétez pas.

La paresse est également quelque chose qui s’avère bien sympathique parfois. Il s’agit de n’évaluer une expression qu’au moment où l’on en aura besoin (affichage du résultat de l’évaluation de l’expression — comme une addition — par exemple). Elle est mise en oeuvre différemment selon les langages (implicitement en Haskell, c’est le compilateur qui gère cela, alors que c’est explicit en OCaml — module Lazy).

Tout cela, rajouté aux syntaxes assez simples, donne une très grande expressivité aux langages fonctionnels, proche de l’expressivité que l’on a en mathématiques. Qui plus est, rappelons-le, les langages fonctionnels sont des langages dits « déclaratifs ». On exprime le problème, on obtient la solution, contrairement à ce que l’on fait dans un langage impératif où l’on doit donner toutes les étapes à la main nous-mêmes.

Bref, beaucoup d’avantages, un tout nouveau style de programmation, cela ne vaut-il pas le détour ? Allez, si vous voulez en savoir plus, voici quelques liens essentiels pour terminer ce billet sur la programmation fonctionnelle.
Cours d’introduction à OCaml de Damien Guichard — dont je me suis beaucoup servi et que j’apprécie beaucoup
Traduction de « A gentle introduction to Haskell » — un peu rude pour débuter, mais vous fera comprendre pas mal de choses.

N’hésitez pas à utiliser le forum pour donner vos avis et retours d’expérience ou pour des questions, bien évidemment !

, , , ,

  1. #1 by Calvin1602 - juin 26th, 2009 at 00:29

    Alp -> Haskell ou OCaml ?
    Autrement dit, est-ce qu’il y a suffisamment de killer features dans Haskell pour que ça vaille le coup d’être appris quand on connait déjà OCaml ?

  2. #2 by Alp Mestan - juillet 16th, 2009 at 16:55

    Oui, définitivement Calvin !

  3. #3 by david - août 18th, 2009 at 07:48

    les classes de types pour surcharger les opérateurs, l’évaluation paresseuse qui encourage d’autant plus la réutilisation sont les deux qui me viennent à l’esprit.

  4. #4 by Alp Mestan - août 20th, 2009 at 10:08

    Même les classes de types pour surcharger les fonctions, tout court, c’est très pratique. Pouvoir faire un show sur n’importe quoi qui instancie Show, c’est bien pratique, i.e comment « sérialiser » en une demie-seconde, par exemple.

    L’évaluation paresseuse est une killer feature mais qui te kill à toi au début. Il faut réussir à la prendre en main et en tirer avantage quand il le faut.

    La FFI de Haskell n’est pas trop mal foutue non plus :) (Foreign Function Interface, i.e ce qui permet de créer des fonctions Haskell dont l’implémentation est en C… ça a permis de faire énormément de bindings de bibliothèques indispensables)

(will not be published)
  1. No trackbacks yet.