Blog Arolla

Impératif vs. fonctionnel ou le comment vs. le quoi

Mon intérêt pour Scala m’a conduit à m’intéresser à la programmation fonctionnelle. J’ai eu une expérience plutôt avec des langages privilégiant le style impératif. Scala permettant à la fois les paradigmes fonctionnel et impératif, je me suis donc posé des questions sur les différences fondamentales entre ces deux paradigmes. Je souhaite, dans ce billet, partager avec vous l’une des distinctions que j’ai pu glaner lors mes lectures diverses.

Le cas d’école

Je vais prendre un exemple qui nous servira de cas d’école. Il s’agira de calculer la somme des éléments d’une liste d’entiers.

Implémentation dans le style impératif

Voyons une solution implémentée dans le style impératif:

1
2
3
4
5
6
def sum(xs: List[Int]) = {
  var sum = 0
  for(x <- xs)
    sum += x
  sum
}

En image cela donnne:

Quelques remarques à propos de cette première implémentation:

  • Nous avons effectué plusieurs affections avant d’obtenir le résultat final c’est-à-dire la valeur de la somme des nombres constituant notre liste.
  • sum += x ne retourne pas de valeur mais a pour effet de bord de modifier la valeur contenue dans la variable sum.
  • La mutation est au centre de cette version de notre programme: sum est une variable mutable c’est-à-dire qui peut changer tout au long de l’exécution du programme. La tâche est accomplie selon un ensemble (une séquence) de commandes: à chaque étape des affectations (modifications d’emplacements mémoire) sont effectuées. Si vous êtes comme moi vous devez être en train de vous dire “Ok ok! Mais je ne vois pas d’ordre ici pourquoi tu me parles de style impératif là?!” Laissez-moi vous guider un peu... L’impératif ici se trouve dans le fait que notre code donne des ordres à l’ordinateur en lui disant à chaque étape quelle valeur il faut affecter à quelle variable : notre programme dit comment calculer la somme des éléments de notre liste.

En résumé dans cette première nous itérons sur la liste, séquençons les modifications et faisons des affectations.

Implémentation dans le style fonctionnel

Voyons maintenant l’implémentation dans le style fonctionnel:

1
2
3
4
def sum(xs: List[Int]): Int = xs match {
  case Nil => 0
  case head :: tail => head + sum(tail)
}

Quelques remarques à propos de cette implémentation:

  • Pas d’affectation
  • La version fonctionnelle consiste à dire à l’ordinateur ce qu’est la somme des éléments de la liste; elle est plus déclarative que la version impérative. Et les programmes écrits dans ce style sont en quelque sorte plus simple car nous n’avons pas à raisonner sur les mutations successives des variables.
  • La récursion: la récursion est omniprésente en programmation fonctionnelle. Au fur et à mesure je fais de la programmation fonctionnelle, je me rends compte j’en fait très souvent usage pour remplacer notamment les boucles, et exprimer plus simplement (élégamment) certains algorithmes. Mon propos n’est pas de confondre le style fonctionnel avec la récursion mais de souligner un usage plus fréquent de la récursion dans ce style. Ceci n’est pas étranger à la proximité de la programmation fonctionnelle et des mathématiques. Rappelez-vous de la représentation de la suite de Fibonacci ou de celle d’une suite arithmétique. L’expression mathématique de ces deux “objets” utilisent la récursion.

Quelques mots de conclusion

Dans cet article je me suis focalisé sur un seul aspect différenciant les paradigmes fonctionnels et impératifs. J’espère revenir dans d’autres billets sur d’autres aspects de la programmation fonctionnelle.
Voici en conclusion quelques points saillants que je retiens de mes récents voyages dans l’univers fonctionnel:

  • La programmation fonctionnelle est une façon de penser, elle peut se traduire même dans des langages hautement impératifs (qui encouragent le style impératif j’entends). Cependant c’est quand même beaucoup plus facile avec un langage fonctionnel (par exemple Haskell).
  • La programmation fonctionnelle n’encourage pas les effets de bord (side effects).
  • La pensée fonctionnelle m’aide à réduire la portée de mes variables et les effets de bord, à composer plus souvent des fonctionnalités.
  • La programmation fonctionnelle promeut la composabilité.
  • La programmation fonctionnelle promeut l’immutabilité et déconseille la mutabilité.
  • L’impératif est plus proche du fonctionnement de la machine: une variable est une partie de la mémoire, sa valeur change au fur et à mesure du déroulement du programme. En fonctionnel on privilégie l'usage des valeurs qui sont immuables; pensez aux variables au sens mathématique.
  • En impératif les étapes du calcul sont détaillées et l’ordre d’exécution est important.
  • En fonctionnel je dis à la machine ce qu’est une opération et je la laisse décider comment la réaliser. Le niveau d’abstraction fait que je n’ai pas besoin de lui préciser toutes les étapes pour qu’elle arrive à accomplir ce que je veux d’elle.
Plus de publications

Comments are closed.