f(x) = y
.
Fonctions
Functions as "first class citizens"
Différents langages dits fonctionnels traduisent plus ou moins cette vision de manière stricte. Ils partagent tous au moins une caractéristique, c'est que les fonctions sont des "citoyennes de première classe", c'est à dire qu'elles font partie des types connus des langages fonctionnels. Une fonction peut donc être :- nommée
- évaluée
- passée comme argument
- renvoyée comme résultat
- utilisée partout où une expression peut l'être.
Fonctions pures
Une fonction mathématique prendx
en paramètre et renvoie y
:
// isVoyelle('a') = true // isVoyelle('r') = false boolean isVoyelle(char c){ ... }Une fonction transforme donc un type de donnée en un autre :
char -- isVoyelle() --> boolean
Et pour une valeur données de x
, f(x)
renvoie toujours la même valeur.
Dans les langages impératifs, à l'intérieur de la fonction
isVoyelle()
, on pourrait aller lire ou écrire dans un fichier, afficher une chaîne à l'écran, modifier une variable globale, tout en renvoyant toujours le même résultat. Une fonction peut avoir des effets de bord, son exécution a toujours lieu dans le cadre global de l'environnement d'exécution du programme.
C'est bien pratique mais ça ne donne pas la garantie qu'une fonction fera toujours exactement la même chose chaque fois qu'on l'appelle, ce qui peut avoir comme effet :
- Compliquer, voir rendre impossible les tests unitaires.
- Compliquer l'exécution parallèle de plusieurs fonctions.
En pratique, quand on programme, les fonctions ne sont pas pures, elles peuvent :
- Renvoyer des erreurs ou exceptions.
- Dépendre de l'extérieur de la fonction (par ex. variables globales, date et heure de l'exécution, disponibilité d'une ressource extérieure).
- Modifier le système (écrire dans un fichier, une base de données).
test()
suivante n'est pas pure.
class ExempleFonctionImpure { private static int i = 0; public static int test(int j){ i += j; return i; } }En effet,
test(1)
va renvoyer 1 au premier appel, puis 2 au second appel etc.
Transparence référentielle
Propriété d'un langage (ou d'un programme) qui fait que toute variable, ainsi que tout appel à une fonction, peut être remplacée par sa valeur sans changer le comportement du programme.
(tiré de www.fil.univ-lille1.fr)
Pour satisfaire cette propriété,
-
Les fonctions doivent être pures.Dans un langage impératif, une fonction est une suite d'instructions qui sont exécutées.
Dans une langage fonctionnel, une fonction est évaluée comme n'importe quelle expression et le langage garantit que le résultat de cette évaluation sera toujours identique. -
Les variables doivent être immutables.Une fois qu'on a déclaré
a = 12
, on peut indifféremment utilisera
ou12
.
Mémoïsation
Si une fonction est pure, pour un argument donné elle renvoie toujours la même valeur.On peut donc fabriquer un cache : enregistrer dans un tableau toutes les valeurs possibles que peut prendre l'argument, avec la valeur de retour de la fonction. On dit qu'on peut la mémoïser.
Que l'on renvoie la valeur calculée ou la valeur stockée dans le cache, on aura le même résultat.
On dit que la fonction est mémoïsable.
En pratique, on ne peut pas le faire pour des types infinis (dont le cardinal de l'ensemble des valeurs du type est infini).
Curryfication
La notion de fonction pure est plus large que la notion mathématique de fonction ; en effet, une fonction pure peut avoir plusieurs arguments en entrée (et dans certains langages renvoyer plusieurs valeurs).Mais on peut transformer une fonction prenant plusieurs arguments en une fonction prenant le premier argument et renvoyant une fonction prenant les arguments suivants. Ce procédé s'appelle la curryfication (le nom vient de Haskell Curry).
Exemple en PHP :
<?php function uncurried_add($x, $y){ return $x + $y; } // Avant PHP 7.4 function curried_add($x){ return function($y) use($x) { return $x + $y; }; } // Depuis PHP 7.4 ("arrow functions") function curried_add2($x){ return fn($y) => $x + $y; } echo "uncurried_add(3, 5) = " . uncurried_add(3, 5) . "\n"; echo "curried_add(3)(5) = " . curried_add(3)(5) . "\n"; echo "curried_add2(3)(5) = " . curried_add2(3)(5) . "\n";(code source)
php curry.php
uncurried_add(3, 5) = 8 curried_add(3)(5) = 8 curried_add2(3)(5) = 8Voir aussi un exemple en java.
Vocabulaire :
Une fonction d'ordre supérieur est une fonction qui prend en paramètre une fonction ou qui renvoie une fonction.
Monades
En pratique, on a besoin des effets de bords - sans eux, on ne pourrait même pas connaître le résultat de l'exécution d'un programme !Comment faire tout en respectant l'intégrité référentielle ?
Les langages fonctionneles "purs" utilisent une construction nommée Monade.