Qu’est-ce que la programmation fonctionnelle ?
Quelques langages fonctionnels et leurs avantages
Lisp et Haskell sont parmi les langages fonctionnels les plus connus. Cependant, leurs forces ne reposent pas sur l’aspect fonctionnel à proprement parlé. Concernant Lisp, sa force réside dans le système de macros permettant d’adapter le langage au domaine du logiciel à développer. Quant à Haskell, c’est également un langage efficace pour le développement, puisqu’il propose un système de types forts qui permet au compilateur de produire du code très optimisé.
Ocaml, dont les racines remontent aux années 70, est un langage fortement typé qui est l’ancêtre des langages que nous allons étudier ici (F#, Rust et Reason ML). Ce langage est aussi multi-paradigme et peut servir à faire de la programmation orientée objet.
Si vous programmez en .NET, vous pouvez utiliser le langage F# qui est une implémentation de Ocaml pour .NET. Puisque F# et C# fonctionnent tous les deux en .NET, il n’est pas difficile de mélanger les deux langages au sein d’un même projet et ils ont tous les deux accès à toutes les librairies écrites pour .NET.
Par exemple, Kaggle a choisi d’utiliser F# plutôt de C# pour les algorithmes d’analyse. Depuis, ils convertissent de plus en plus leur code C# en F# car ils le trouvent plus court, plus facile à lire et plus facile à remanier. De plus, grâce aux types forts le code contient beaucoup moins de bugs.
Des langages à la mode
Vous avez sûrement entendu parler de Rust, le langage développé par Mozilla et qui veut remplacer le C, mais vous ignorez peut-être que la première version de Rust a été écrite en Ocaml et qu’il en garde bien des aspects. Rust emprunte aussi la technique RAII (resource acquisition is initialisation) à C++, tout en l’étendant afin de pouvoir se passer de ramasse miettes (garbage collector). Un logiciel en Rust n’a donc jamais besoin de se mettre en pause pour nettoyer la mémoire utilisée. Par conséquence, une performance stable est facile à garantir. Rust oblige à déclarer quelles parties du code peuvent modifier telle valeur et le compilateur vérifie qu’aucune autre partie ne peut accéder à cette valeur pendant sa modification. Cela protège des erreurs quand on écrit du code qui va être exécuté en parallèle. C’est sûrement pour ces raisons que, depuis 2016, Rust gagne chaque année le prix du langage le plus aimé dans les sondages de StackOverflow.
Autre exemple, Mozilla est en train de ré-implémenter Firefox en Rust. C’est loin d’être fini mais leur équipe a déjà pu remplacer le code qui applique les styles CSS aux éléments HTML par un code écrit en Rust. Cette tâche peut, en théorie, être faite en parallèle mais c’est seulement avec les garanties fournis par Rust qu’ils ont enfin pu le faire. Le résultat ? Un code bien plus rapide et plus sûr que le code existant écrit en C++.
Nous avons déjà mentionné ReasonML. Il s’agit d’une nouvelle syntaxe pour le langage Ocaml accompagné par un compilateur qui le transforme en javascript ordinaire. De ce fait, un module écrit en ReasonML peut être utilisé par du code écrit en javascript, et à l’inverse ReasonML peut facilement utiliser un module existant écrit en javascript. Le code javascript produit par le compilateur est assez lisible et souvent bien plus performant que du javascript écrit à la main car les types forts permettent certaines optimisations qui seraient difficile à réaliser à la main.
Des types forts pour se protéger des erreurs
Quand on crée une valeur de type Temperature on doit indiquer l’unité de mesure.
La valeur numérique est étiquetée et englobée par son unité de mesure, et on ne peut plus l’utiliser directement. Pour utiliser une valeur de type Temperature on doit traiter tous les cas possibles. Exemple ci-joint.
La syntaxe “match” fonctionne comme un super “switch case”, qui oblige à traiter tous les cas. Ainsi, le jour où on ajoutera les températures en Kelvin, il suffira d’ajouter l’unité Kelvin au type Temperature et tout de suite le compilateur indiquera tous les endroits où le code devra traiter les températures en Kelvin.
Des règles métiers forment souvent un genre de machine d’état, et si on les implémente avec ce genre de type, on peut être sûr que tous les cas ont été traités. De plus, quand il faudra ajuster les règles métiers, le refactoring sera facile puisque le compilateur apporte son aide.
Eviter les NullReferenceException
Les types forts peuvent nous aider via le type Option défini ainsi.
| Some(T)
| None
Conclusion
Pour les objets connectés en particulier, Rust pourra bientôt être un choix solide pour le développement. D’une part, parce qu’un logiciel écrit en Rust est performant et peu gourmand en mémoire, et d’autre part, parce que ce langage aide à produire un code fiable.