Code Intro : les paradigmes

Il existe plusieurs façons d’écrire et d’organiser son code, chacune très différentes bien que souvent mélangées, notamment dans les langages modernes. Chaque paradigme propose une façon qui lui est propre de résoudre un problème. Il en existent des dizaines, certains découlant d’autres.

Les moyens ou la fin

Avant même de parler de sous-type, parlons déjà des deux grands paradigmes que vous croiserez, l’impératif et le déclaratif :

  • le paradigme impératif consiste à dire comment le programme doit procéder pour obtenir notre résultat ;
  • le déclaratif consiste quant à lui à exprimer quel résultat est attendu sans ce soucier de comment le programme va procéder.

J’ai essayé de trouver des exemples convaincants et clairs mais sans succès. Nous allons prendre deux languages pour exemple : SQL pour le déclaratif, et PHP pour l’impératif. Pour cette exemple, imaginons que nous partons exactement du même genre de données, de deux tableaux possédant les mêmes données : une liste de personnes et le pays dans lequel elles résident. Nous souhaitons récupérer le nom des dix premières personnes vivant en France que l’on trouvera. Pour PHP nous ferons :

foreach ($users as $user) {
  if ($user['country'] == "France"]) {
    $result[] = $user["name"];
    
    if (count($result) == 10) break; 
  }
}

En SQL :

SELECT name FROM users WHERE country = "France" LIMIT 10;

La principale différence ses deux exemples est que le premier est impératif, le second déclaratif. En effet, dans le code PHP nous avons écrit pas à pas comment la machine doit procéder, un peu à la façon d’une recette de cuisine :

  • tout d’abord elle devra un à un traverser tout les éléments contenus dans le tableau ;
  • lorsqu’une ligne du tableau correspond à notre critère, elle l’ajoutera à un tableau de résultats ;
  • si on a obtenu dix résultats, on cesse de traverser le tableau.

A l’inverse, le code SQL lui ne donne absolument aucune indication de comment obtenir notre résultat, et se contente tout simplement de décrire ce qui doit être obtenu. De fait, tout code exécuté par le processeur sera impératif, mais dans le cas d’un langage déclaratif, le développeur laisse à son langage le soin de déterminer comment procéder. En ce sens, les langages déclaratifs sont donc plus abstraits.

En fin de compte, utiliser un langage déclaratif est comme avoir quelqu’un pour nous rendre service : si on souhaite un jus d’orange, on exprime notre demande et on obtient notre verre sans même avoir à penser comment il a été préparé, alors qu’en langage impératif on ordonnera à quelqu’un de prendre des oranges, de les presser, et de nous rapporter le verre une fois plein !

Procédural, orienté objet et fonctionnel

Dans les faits, vous entendrez plus souvent parler de ces trois “sous-paradigmes”. Les deux premiers sont impératifs, le troisième déclaratif. Voyons rapidement les principales différences notables du point de vue du codeur !

Le procédural

Le plus simple paradigme de type impératif est le procédural. Ici, il ne sera question que de de faire exécuter divers procédures, généralement contenues à l’intérieur de fonctions réutilisables (on parle alors de factorisation). Le code PHP écrit plus haut pourrait ainsi être reécrit de façons procédurale :

global $users;

function getUsersByCountry($country) {
  foreach ($users as $user) {
    if ($user['country'] == "France"]) {
      $result[] = $user["name"];
    
      if (count($result) == 10) break; 
    }
  }

  return $result;
}

$frenchUsers = getUsersByCountry("France");

J’ai sciemment fait de la variable $users une globale, nous verrons plus tard pourquoi. Dans tout les cas, le principe est des simple, nous pouvons écrire des fonctions (ou procédures), que l’on pourra appeler à n’importe quel moment, ou à l’intérieur même d’autres fonctions. Dans l’exemple ci-dessus, on fait appel à la fonction count(array) (qui est une fonction de base de PHP).

L’impératif était le paradigme le plus rependu avant que l’orienté objet ne submerge le monde du développement. C est impératif (C++ intégrant l’objet), la plupart des langages shell sont impératifs. PHP était principalement impératif jusqu’à la version 5 qui a concrétisé une lente implémentation des fonctionnalités objet !

L’orienté objet

Autre paradigme dérivé de l’impératif, la programmation orienté objet (POO) est à l’heure actuel la plus répandue et le plus utilisée. Si il est plus complexe que le procédurale, le principe de base est pourtant simple : nous souhaitons pouvoir manipuler des objets qui contiendrons (on dit alors “encapsuler”) un ensemble de logique et de données formant un tout cohérent. Toujours en PHP, notre exemple pourrait ainsi devenir :

class Users
{
  private $users = [];

  public function __construct(array $users) {
    $this->users = $users;
  }

  public function getUsersByCountry($country, $limit = 10) {
    foreach ($this->users as $user) {
      if ($user['country'] == "France"]) {
        $result[] = $user["name"];
    
        if (count($result) == $limit) break; 
      }
    }
    return $result;
  }
}

$usersList = new Users($users);
$frenchUsers = $usersList->getUsersByCountry("France");

Notre objet est ici déclaré par le bloc class Users, et que l’on instancie via new Users($users). Nous constatons que cet objet contient à la fois des données, le tableau $users que nous lui passons lors de son instanciation, ainsi que de la logique, la fonction getUsersByCountry. Alors oui, sur ce simple exemple, on semble effectivement écrire beaucoup pour pas grand chose, et c’est le cas ! Mais ce style de programmation a justement été pensé pour pouvoir mieux organiser et maintenir le code de grosses applications.

Toutefois, l’orienté objet peut très vite être mal utilisée sans une large connaissance et expérience avec ce style. C’est un paradigme très vaste qu’il est difficile de maîtriser complètement, et qui demande régulièrement de se poser les bonnes questions pour prendre les bonnes décisions. Malgré sa prédominance, je ne prend aucun risque à dire que la POO est très mal comprise, y compris par la majorité des personnes l’utilisant au quotidien (moi compris sans doute !).

Le fonctionnel

Seul paradigme déclaratif, le fonctionnel est une tout autre bête que les deux précédents, avec lesquels il ne partagent rien sur le papier. Il repose sur une idée totalement mathématique du code : une fonction doit toujours retourner un résultat prédictible en fonction des arguments que l’on lui passe, et uniquement eux. Si nous avons la formule y=2x+1, y sera toujours égale à 7 lorsque x est égale à 3.

En fonctionnel, tout est fonction, et toutes fonctions sont totalement fermées au contexte dans lequel elles sont invoqués. Vous vous souvenez lorsque j’ai utilisé une variable globale pour exemplifier le procédural, et ainsi y accéder depuis l’intérieur de ma fonction ? Il serait impossible d’y accéder en fonctionnel !

def get_users_by_country(users, country, limit) do
  Enum.filter(users, fn(x) -> x.country == country end)
  |> Enum.take(limit)
end

frenchUsers = get_users_by_country(users, "France", 10)

Ceci est une fonction dite pure en Elixir : pour une même liste d’utilisateurs, un même pays et une même limite, j’obtiendrai strictement toujours le même résultat.

L’idée de pureté est centrale : on éradique ce que l’on appelle l’effet de bord (side-effect en anglais), tout ce qui pourrait influer sur le résultat depuis l’extérieur de la fonction. Dans notre exemple procédural, le résultat de la fonction ne dépend pas uniquement des arguments passés, mais aussi de la valeur de la globale $users.

Le fonctionnel a toutefois un gros défaut : il est beaucoup plus difficile à apprendre. Ceci s’explique par plusieurs raisons :

  • les développeurs déjà habitués au procédural/POO sont totalement désorienté face à une logique différente ;
  • il existe beaucoup moins de ressources accessibles pour l’apprendre seul dans son coin ;
  • quand les ressources existent, elles ont souvent de quoi décourager plus d’une personne.

Je précise ma pensée sur ce dernier point. Puisque ce paradigme est très “mathématique” et qu’il a principalement été reclus au monde académique, le vocabulaire employé et les moyens de l’expliquer sont très souvent cryptiques, sinon élitistes. On recommande souvent Haskell comme porte d’entrée car il est le “plus pur des langages fonctionnel”. En ce qui me concerne, Elixir me paraît beaucoup plus abordable même si “moins pur” car la documentation et la communauté en générale sont plus accessibles. Lorsque l’on apprend, on a plus souvent besoin de comprendre concrètement le pourquoi du comment, or avec le fonctionnel on a parfois l’impression de lire un cours de mathématique… Si en détournant le regard d’un tutoriel, et qu’après 30s vous y revenez avec l’impression de lire une cours sur la relativité général, changez de tuto !

C’est quoi le mieux ?

A vrai dire, comme pour le choix d’un langage : aucun, il est question de besoin, d’envie, de curiosité ! Les langages orienté objet intègrent aussi de plus en plus de concepts provenant du fonctionnels (Javascript par exemple avec React), et à l’inverse certains langages fonctionnels se sont ouverts à l’orienté objet (Ocaml).

Il y’a une chose très importante à garder en tête selon moi, et que mon oncle Scott résume très bien : don’t believe the hype ! Rien ne “changera votre vie” (un peu comme l’iPhone 16 ou la 12e nouveau modèle de Tesla). Ce sont des outils. Certains vous paraîtront plus intéressants, plus pratiques dans certains cas, plus plaisants à utiliser, mais ils reste de simple outils !

Il existe en outre un besoin très présent en informatique de “prêcher pour sa paroisse” : que ce soit Linux, Python, le fonctionnel, React, un énième nouvel IDE, les webcomponents, le fait de n’utiliser que VIM… On sur-vend bien souvent les mérites d’une chose sans réelle objectivité. A l’inverse, vous trouverez nombre de critiques injustifiées sur un tas de choses, le plus souvent par manque de connaissance, ou parce qu’on prêche pour une autre paroisse bien sûr !

Comme il sera plus ou moins adapté d’utiliser tel langage dans certaines conditions précise pour répondre à un besoin en particulier dans un cadre qui a ses contraintes propres, les paradigmes n’y échappent pas. La POO a ses atouts, ses défauts, et ses pièges à éviter. Vous tomberez alors peut-être sur moult article clamant que “ALLELUIA ! Le fonctionnel est plus puissant, plus compréhensible, plus maintenable et plus sûr !” Les personnes qui disent cela ont des années d’expériences, ils ne peuvent pas avoir tort. Puis on trouvera peut être d’autres personnes, qui seront plus mitigés, qui diront “oui mais…”. D’autres encore ne seront pas du tout d’accord… Après tout, si on retrouve du fonctionnel dans la POO, et de la POO dans le fonctionnel, on ne peut qu’espérer que ce n’est pas dans l’unique but d’attirer “ceux de l’autres bord”, mais bien pour combler certains besoins.

Bref : soyez éblouis par l’un, l’autre, ou les deux, mais veillez à ne pas devenir aveugle !

Leave a Reply

Your email address will not be published. Required fields are marked *