Fermeture (informatique)

Un article de Wikipédia, l'encyclopédie libre.

Pour les articles homonymes, voir Fermeture.

Dans un langage de programmation, une fermeture ou clôture (en anglais, closure) est une fonction qui capture des références à des variables libres de son environnement lexical qui ne soient pas des variables globales. Une fermeture est donc créée lorsqu'une fonction est définie dans le corps d'une autre fonction et fait référence à des variables locales ou des arguments de la fonction dans laquelle elle est définie.

Une fermeture peut être passée en paramètre d'une fonction dans l'environnement où elle a été créée (passée vers le bas) ou renvoyée comme valeur de retour (passée vers le haut). Dans ce cas, le problème posé alors par la fermeture est qu'elle fait référence à des données qui auraient typiquement été allouées dans la pile, et désallouées à la sortie de l'environnement. Hors optimisations par le compilateur, le problème est généralement résolu par une allocation sur le tas de l'environnement.

Exemple de fermeture en Javascript.

La fonction interne ajoute10 a toujours accès à l'argument nombre malgré le fait que l'appel à la fonction ajouteur soit terminé :

function ajouteur(nombre) {
  function ajoute(valeur) {
    return valeur + nombre;
  }
 
  return ajoute;
}
 
var ajoute10 = ajouteur(10);
ajoute10(1); // retourne 11

Exemple, en Common Lisp, d'une fonction qui renvoie un prédicat, vrai si son argument est plus grand qu'un certain minimum :

(defun créer-prédicat-plus-grand-que (min)
     (lambda (x)
       (> x min)))

La fermeture créée dans créer-prédicat-plus-grand-que capture dans son environnement lexical la variable min.

La même fonction en OCaml :

let creer_predicat_plus_grand_que = function
    min -> ( fun x -> x > min)

qui donne :

let sup10 = creer_predicat_plus_grand_que 10;;
sup10 12;;   (* true  *)
sup10 8;;    (* false *)

OCaml permet également de capturer dans une fermeture une valeur modifiable en place (mutable). Par exemple, pour créer un compteur, on définit simultanément 3 fonctions :

let raz, inc, compteur = (* remise à zéro, incrémentation, interrogation *)
  let n = ref 0 in
  (function () -> n:=0),       (* raz = remise à zéro  *)
  (function () -> n:= !n + 1),  (* inc = incrémentation *)
  (function () -> !n)           (* compteur = interrogation  *)

la variable mutable n est capturée dans l'environnement commun des 3 fonctions raz, incr et compteur, qui s'utilisent de la sorte :

compteur();;  (* renvoie 0 *)
inc();;       (* incrémente, ne renvoie rien *)
compteur();;  (* renvoie 1 *)
inc();inc();inc();; (* compteur vaut maintenant 4 *)
raz();;
compteur();;  (* renvoie 0 *)
n;;           (* renvoie "Unbound value n" car n est encapsulée *)