Cet article est une suite à l’article sur les concepts fonctionnels, suggérée par
@pcolomb. Comme je
n’ai pas encore assez regardé les lambda dans Java 8 pour ajouter des exemples
dans l’article, j’ai pensé présenter un moyen de simuler ces concepts en pur Java.
La version proposée n’est
évidemment pas très utilisable, et probablement pas performante, mais elle peut
aider à saisir le fonctionnement de cette machinerie (et en plus c’est
rigolo).
Comme présenté dans l’article précédent, les lambda sont des fonctions
anonymes, qui peuvent être crée à la demande. Nous allons créer ici une classe
permettant de simuler une telle fonction unaire.
On veut donc réer une classe générique qui simule la création d’une fonction
anonyme de signature A → B. Ceci peut s’exprimer comme suit :
Ça marche ! Par soucis d’intégration, il serait plus pratique que notre
« fonction » implémente Callable, ce qui permettrait de l’utiliser
dans un contexte où cette interface standard est requise. Essayons de modifier
notre classe en conséquence. L’argument ne pouvant être passé à notre méthode
call, le seul moyen est de le garder dans l’état interne de notre
objet, sous forme d’un attribut privé.
Notez la méthode Function<A,R> bind(A arg), qui à un petit
goût de fluent interface, qui nous permet de facilement chaîner les
méthodes. Cette méthode bind revient à effectuer une
partialisation de notre fonction, qui peut donc être appelée sans
argument, et répond donc à Callable. Cette application partielle
correspond à une fermeture, la variable close étant ici stockée dans un
attribut privé de notre objet fonction. Enfin, un peu de surcharge de
call peut rendre tout ça plus facile d’utilisation, tout en
maintenant la compatibilité avec l’interface (attention toutefois à l’effet de
bord interne, puisque l’état de notre objet est modifié).
On vient donc de simuler une fermeture nous permettant de faire une
application partielle.
Notre objet Function nous permet pour l’instant de créer des
fonctions à un seul argument, ce qui est relativement limité. Comment peut on
alors définir des fonctions à plusieurs arguments ? On pourrait créer une
classe générique pour chaque arité de fonction, mais il y a une réponse plus simple :
créer des fonctions curryfiées,
c’est-à-dire une fonction d’un seul argument retournant une fonction d’un seul
argument retournant une fonction d’un seul argument, etc.
On peut d’ores et déjà le faire « à la main » avec ce dont on dispose. Créons
ainsi une fonction add: Interger → Integer → Integer avec notre
première version de lambda.
Pas très lisible, mais ça marche. Notez que les arguments (au moins
a) doivent être finaux, puisqu’ils sont utilisés dans une classe
interne anonyme ; mais c’est pas grave, on fait du fonctionnel non ?
Utilisons la version Callable de notre
Function :
Ouch ! Ça pique un peu, mais ça marche, et on récupère les fonctionnalités
précédentes.
Et si l’on veut les deux ? La relative « facilité » de définition de la
première approche, avec la plus grande flexibilité de la seconde ? En objet, on
ferait de la délégation, en fonctionnel, on parle de
composition, mais le principe est strictement le même. Nous allons
créer une « fonction », qui prendra une fonction et en retournera une
application partielle, augmentée des méthodes de la deuxième approche. Cela
correspond au patron décorateur d’un point de vue fonctionnel (c’est
d’ailleurs le nom que ce type d’approche porte en Python). On reprend donc la
même approche, mais au lieu de redéfinir callMe, on définie un
attribut fun contenant une fonction qui sera appelée avec la valeur stockée dans
arg (d’où la délégation).