Programmation fonctionnelle avec Elixir

by Alexandre Dedourges, DevSec

Vous vous demandez peut-être ce qu’est un langage de programmation fonctionnel ? Ou peut-être souhaitez-vous en apprendre plus ? Dans cet article, nous allons parler de la programmation fonctionnelle et plus particulièrement de la programmation fonctionnelle avec Elixir. Ce dernier est un langage ayant une syntaxe assez simple à comprendre. Il vous permettra de vous familiariser de manière plus aisée avec la programmation fonctionnelle. Nous verrons aussi quels sont les avantages de ce paradigme de programmation. 

La programmation fonctionnelle, c’est quoi ?

Les paradigmes de programmation

La programmation fonctionnelle est une façon d’aborder la programmation informatique. On appelle cela un paradigme. Les cinq paradigmes les plus représentés et utilisés sont les suivants : 

  • Programmation impérative

  • Programmation procédurale

  • Programmation fonctionnelle

  • Programmation déclarative

  • Programmation orientée objet

Chaque paradigme a sa propre façon de fonctionner, ici nous allons donc nous concentrer sur la programmation fonctionnelle. Le plus gros avantage de celle-ci est qu’elle va permettre de tirer avantage au mieux des processeurs actuels. En effet, la programmation fonctionnelle va pouvoir utiliser au mieux la concurrence des processus et le parallélisme qu’offrent ceux-ci. Cela afin d'obtenir des programmes plus rapides, le temps de réponse de ceux-ci étant un élément extrêmement important de nos jours. 

Pourquoi certains paradigmes limitent l’utilisation des processeurs ?

Certains paradigmes comme celui de la programmation impérative ne permettent pas une utilisation aussi poussée du processeur que le permettrait un langage fonctionnel comme Elixir. En effet, les langages impératifs ont pour principal désavantage d’avoir des valeurs partagées qui peuvent être modifiées. Imaginons par exemple une simple liste comme celle-ci :

list = [“A”, “B”, “C”]

Pour la modifier, nous avons des fonctions à notre disposition, par exemple :

List.delete(list, “C”)

En utilisant une fonction de ce type dans un langage impératif, notre liste "list" prendrait la valeur [“A”, “B”]

En Elixir (qui est un langage de programmation fonctionnelle), la liste "list" aurait toujours la même valeur, c'est-à-dire [“A”, “B”, “C”]. Néanmoins, le résultat de la fonction List.delete(list, “C”) renverra bien comme résultat [“A”, “B”]

Ces deux concepts sont donc bien différents. Maintenant, imaginez plusieurs fonctions qui manipulent la valeur de notre liste "list" et toutes en même temps. Le risque d’erreur ou de comportement inattendu est énorme dans un langage impératif, car la valeur de la liste peut changer à tout moment. C’est pourquoi il est beaucoup plus compliqué de tirer avantage des processeurs actuels avec ce type de langage.

À l’inverse, en programmation fonctionnelle, la valeur de la liste ne changera jamais, elle est immuable. Ce qui rend son utilisation possible de manière concurrente et en parallèle. Plusieurs fonctions ou processus peuvent manipuler la liste sans risque, car sa valeur ne changera pas de manière inattendue. Les langages fonctionnels ont par ailleurs pour but d’utiliser au mieux les fonctions pour créer des logiciels propres et maintenables. De plus, ils sont déclaratifs, c'est-à-dire que l'appel d'un de leurs composants avec les mêmes arguments produit exactement le même résultat, quels que soient le moment et le contexte de l'appel.

Elixir, un langage de programmation fonctionnel avec une syntaxe simple

Elixir est un langage de programmation fonctionnel bien qu’il soit aussi considéré comme un langage multiparadigme. Celui-ci fonctionne sur machine virtuelle Erlang (BEAM) qui permet aux applications écrites en Elixir de fonctionner. Son créateur, José Valim, désirait créer un langage de programmation pour les sites et applications à grande échelle. Il a été créé pour tirer profit au maximum des processeurs multicœurs qui ont commencé à se démocratiser pleinement à partir des années 2000. Il est particulièrement utilisé pour créer des systèmes distribués à faible latence et tolérants aux pannes.

Une place importante pour les fonctions

En programmation fonctionnelle, les fonctions occupent une place très importante. En effet, chaque fonction va recevoir en entrée des données, ces données seront ensuite traitées par la fonction (calcul, mis en forme…) puis le résultat de ces opérations sera retourné en sortie. Ce sont ces fonctions qui vont former votre programme. Étant donné que les fonctions peuvent très vite se multiplier et se complexifier, il est recommandé de créer des fonctions ayant une seule tâche. De plus, chaque tâche doit être la plus simple possible. Enfin, chaque fonction doit être explicite, on doit facilement comprendre quel est son rôle. 

La meilleure méthode pour créer les programmes les plus “propres” possibles est d’utiliser des fonctions “pures”. Pour qu’une fonction soit considérée comme pure, elle doit respecter trois règles qui sont les suivantes :

  • La fonction ne génère pas d'effets en dehors de la valeur qu'elle renvoie.

  • Le résultat de la fonction n'est affecté que par les arguments de la fonction.

  • Les valeurs sont immuables.

Si ces trois règles sont respectées alors la fonction est dite pure. C’est le cas par exemple d’une fonction qui effectue des calculs simples comme celle-ci :

code

Ici, le résultat de la fonction est prévisible, de plus la fonction ne génère aucun effet en dehors de la valeur qu’elle renvoie et enfin le résultat n’est affecté que par les arguments donnés en entrée.

À l'inverse, les fonctions impures renvoient des résultats qui peuvent être imprévisibles. Nous verrons ceci plus tard. 

De plus, en programmation fonctionnelle et en Elixir les fonctions ont une place tellement importante qu’il est possible d’utiliser une fonction en argument d’une autre fonction ! 

Prenons par exemple cet exemple : 

code

Dans cet exemple, on peut voir que nous avons une fonction Enum.map, cette fonction prend ici 2 paramètres. Le premier est une liste d’entier et le deuxième est une fonction (Integer.to_string). Vous l’aurez peut-être déjà compris, nous voulons transformer chacun des entiers de la liste en string. La fonction Enum.map comprend alors que nous voulons appliquer la fonction Integer.to_string à chacun des éléments de la liste. Le résultat est donc une liste d'entiers convertis au format string.

code

Vous pensez peut-être maintenant que le code va vite devenir illisible si nous commençons à utiliser des fonctions dans des fonctions. C’est sans compter l’intervention de l'opérateur “pipe”. En Elixir il existe en effet un opérateur qui va nous permettre de simplifier l’utilisation de plusieurs fonctions les unes à la suite des autres. Cela rendra le code plus lisible et surtout plus compréhensible.

Dans beaucoup de langage, vous pouvez utiliser des résultats de fonctions dans d’autres fonctions comme ceci :

code

OU si vous souhaitez rendre votre code un peu moins complexe :

code

En Elixir, il existe une bien meilleure façon de faire grâce au pipe ! L’opérateur pipe se présente sous cette forme “|>”. Celui-ci peut s’utiliser de la manière suivante :

code

Comme vous pouvez le voir, le code est grandement simplifié et on comprend vite ce qui est exécuté. Le résultat de la fonction_3 est passé en paramètre de la fonction_2 et le résultat de cette fonction est passé à la fonction_1. Le résultat final est exactement le même qu’avec les deux exemples précédents, mais avec un code bien plus lisible.

Enfin, il existe une différence majeure entre les langages fonctionnels et les langages impératifs. En effet, Elixir et tous les autres langages fonctionnels sont des langages dits déclaratifs. Ce qui signifie qu’ils se concentrent sur q**uoi **faire pour résoudre un problème tandis que les langages impératifs se concentrent sur c**omment **faire pour le résoudre.

Bien souvent en se concentrant sur quoi faire nous avons besoin de moins de code pour arriver au même résultat qu’en réfléchissant à comment le faire. C’est donc un avantage de taille puisque moins de code veut aussi dire moins de temps, moins de difficultés…

La programmation fonctionnelle, une manière différente de penser la programmation

Si vous n’avez jamais fait de programmation fonctionnelle, la transition peut être compliquée pour vous et c’est normal ! Pour passer d’un paradigme à un autre, il faut revoir toute sa façon de penser. En passant d’un langage orienté objet à un autre, nous n’avons pas de mal à nous adapter. La grande difficulté est de passer d’un paradigme à un autre. Mais une fois que vous avez compris les différents mécanismes, vous pourrez tirer pleinement avantage des langages fonctionnels et de tout ce qu’ils apportent avec eux ! Bien évidemment, utiliser un langage de programmation fonctionnel n’est pas forcément toujours la solution la plus adaptée en fonction de vos usages. Le mieux est donc encore de maîtriser plusieurs paradigmes. Néanmoins, la programmation fonctionnelle apporte son lot de caractéristiques bien pratique. 

Chez Cryptr, nous utilisons la programmation fonctionnelle et plus particulièrement Elixir, pour développer nos applications. Si vous souhaitez en apprendre plus, n’hésitez pas à nous contacter, à consulter nos autres articles ou encore à visiter nos réseaux sociaux comme LinkedIn, ou Twitter !

Add enterprise SSO for free

Cryptr simplifies user management for your business: quick setup, guaranteed security, and multiple free features. With robust authentication and easy, fast configuration, we meet businesses' security needs hassle-free.

More articles

SAML vs SSO: Differences between SSO and SAML authentication

Uncover the key differences between SAML vs SSO in user authentication. How SAML enables SSO and their roles in enhancing identity security and login processes

Read more

A guide of Magic Link Login for Passwordless Authentication

Unlock passwordless authentication with email magic links! boost security and user experience. Discover our comprehensive guide to email magic link login

Read more