Il est possible de renvoyer des fonctions en tant que donnée. De cette manière, vous pouvez écrire une fonction qui construit des fonctions dans un but précis en fonction de certains paramètres. Le point délicat est de savoir quelles variables sont visibles par la fonction construite. La façon dont cela fonctionne dans GEL est que, lorsqu'une fonction renvoie une autre fonction, tous les identifiants qui étaient référencés dans le corps de la fonction mère, et qui deviendraient hors de portée, sont en fait ajoutés dans un dictionnaire privé de la fonction renvoyée. Ainsi la fonction voit toutes les variables qui étaient à sa portée au moment où elle a été définie. Par exemple, nous pouvons définir ainsi une fonction qui renvoie une fonction qui ajoute 5 à son argument :
function f() = ( k = 5; `(x) = (x+k) )
Notez que la fonction ajoute k
à x
. Vous pouvez l'utiliser ainsi.
g = f(); g(5)
et g(5)
doit renvoyer 10.
Une chose à noter est que la valeur de k
utilisée est celle qui est en cours lorsque la fonction f
a terminé son exécution. Par exemple :
function f() = ( k := 5; function r(x) = (x+k); k := 10; r )
renvoie une fonction qui ajoute 10 à son argument plutôt que 5. La raison est que le dictionnaire supplémentaire est créé uniquement lorsque le contexte dans lequel la fonction a été définie, se termine, c'est-à-dire lorsque la fonction f
renvoie. C'est cohérent avec la manière dont on s'attend à ce que la fonction r
fonctionne à l'intérieur de la fonction f
d'après les règles sur la portée des variables dans GEL. Les seules variables ajoutées au dictionnaire supplémentaire sont celles qui sont présentes dans le contexte qui se termine et n'existe plus. Les variables utilisées dans la fonction, qui sont toujours dans des contextes valides, fonctionnent comme d'habitude, en utilisant la valeur actuelle de la variable. La seule différence, c'est pour les variables et les fonctions globales. Tous les identifiants qui référencent des variables globales au moment de la définition de la fonction ne sont pas ajoutés au dictionnaire privé. C'est pour éviter beaucoup de travail non nécessaire lors du renvoi de fonctions et cela ne pose que rarement problème. Par exemple, supposons que vous supprimiez le « k= 5 » de la fonction f
et qu'au niveau supérieur vous définissiez k
à 5 par exemple. Alors lorsque vous exécutez f
, la fonction r
ne met pas k
dans le dictionnaire privé parce qu'il était global (au niveau supérieur) au moment de la définition de r
.
Parfois il est préférable d'avoir un meilleur contrôle sur la façon dont les variables sont copiées dans le dictionnaire privé. Depuis la version 1.0.7, vous pouvez spécifier quelles variables sont copiées dans le dictionnaire privée en mettant des crochets carrés supplémentaires après les arguments contenant la liste des variables qui seront copiées, séparées par des virgules. Si vous faites cela alors les variables sont copiées dans le dictionnaire privé au moment de la définition de la fonction et le dictionnaire privé n'est pas modifié ensuite. Par exemple,
function f() = ( k := 5; function r(x) [k] = (x+k); k := 10; r )
renvoie une fonction qui, lorsqu'elle est appelée, ajoute 5 à ses arguments. La copie locale de k
a été créée lorsque la fonction a été définie.
When you want the function to not have any private dictionary
then put empty square brackets after the argument list. Then
no private dictionary will be created at all. Doing this is
good to increase efficiency when a private dictionary is not
needed or when you want the function to lookup all variables
as it sees them when called. For example suppose you want
the function returned from f
to see
the value of k
from the toplevel despite
there being a local variable of the same name during definition.
So the code
function f() = ( k := 5; function r(x) [] = (x+k); r ); k := 10; g = f(); g(10)
will return 20 and not 15, which would happen if
k
with a value of 5 was added to the private
dictionary.