Tu maîtrises les fonctions, mais qu’en est-il des "functors" ?
La programmation fonctionnelle est à la mode en ce moment. Tout le monde en parle. Si vous aussi vous avez commencé à fouiner un peu vous êtes surement déjà tombé plusieurs fois sur ce mot : "functor".
On est bien d’accord que le titre de cet article n’existe que pour la ressemblance des mots fonction et functor. On ne va pas du tout parler des fonctions ici. Pas directement en tout cas.
Qu'est-ce qu'un functor
C’est si simple que vous allez être déçu. Un functor est :
- un objet
- qui implémente la méthode
map
Voilà. Oui, c’est tout.
Les secrets de la méthode map
Bon ceci dit, vous vous doutez bien que la méthode map
doit répondre à quelques règles précises et qu'il ne suffit pas d'appeler une méthode "map" pour que l'objet qui la possède devienne comme par magie un functor. Sinon quel intérêt ? D'ailleurs elle n'est pas obligée de s'appeler "map" cette méthode.
Map doit être telle que F(f) : F(a) => F(b)
La méthode map
sert à faire un mapping, à transformer un functor. Elle transforme, dans l'exemple ci-dessus, a
en b
. La règle précise juste que si F
est un type de functor, sa méthode map
retournera peut-être des données modifiées, mais encapsulées dans un functor de même type F
.
Map ne modifie pas l'instance principale
La méthode map
d’une instance de F
retournera toujours une instance de F
. Sauf que si elle ne doit pas modifier l'instance principale tout en ayant une action sur les valeurs de l'instance retournée, on comprend vite que map
retourne toujours une nouvelle instance.
Ce qui fait de map
une méthode "pure". Pour les mêmes valeurs d'entrées j'obtiendrais toujours les mêmes valeurs de sortie. Elle ne cache pas de side effects. Elle ne modifie pas les valeurs d'entrées.
F(a)
etF(b)
possède le même nombre de connexions
L'instance retournée par map
doit posséder le même nombre de connexions que l'instance principale. Les connexions sont les valeurs modifiées par map
. C’est un peu flou dit comme ça mais ce sera sûrement plus simple avec un exemple.
Les functors en JavaScript
Un exemple de functor bien connu en JavaScript est Array
. Cet objet possède une méthode map
qui permet de modifier les valeurs contenues dans le tableau.
[1, 2].map(x => x * 2) // => [2, 4]
[1, 2].map(x => String(x)) // => ["1", "2"]
La méthode Array.map
retourne toujours une instance de Array
même si les valeurs contenues ne sont plus de même type.
let a = [1, 2]
let b = a.map(x => x * 2)
console.log(a) // [1, 2]
console.log(b) // [2, 4]
Les valeurs de a
n’ont pas changées après l’utilisation de Array.map
alors que l'on a bien les bonnes (nouvelles) valeurs dans b
.
let a = [1, 2]
let b = a.map(x => x * 2)
a.length === b.length // true
Ce dernier exemple paraît bête, mais il illustre bien le propos. Array.map
retournera toujours un Array
avec le même nombre de connexions (ou d’éléments ici).
Bien d’autres objets sont des functors en JavaScript. Observez par exemple le fonctionnement de Promise
et essayer de trouver comment il respecte les règles sur les functors.
Pour aller plus loin :