7.3. Returning Functions

It is possible to return functions as value. This way you can build functions that construct special purpose functions according to some parameters. The tricky bit is what variables does the function see. The way this works in GEL is that when a function returns another function, all identifiers referenced in the function body that went out of scope are prepended a private dictionary of the returned function. So the function will see all variables that were in scope when it was defined. For example, we define a function that returns a function that adds 5 to its argument.

function f() = (
  k = 5;
  `(x) = (x+k)
)
Notice that the function adds k to x. You could use this as follows.
g = f();
g(5)
And g(5) should return 10.

One thing to note is that the value of k that is used is the one that's in effect when the f returns. For example:

function f() = (
  k := 5;
  function r(x) = (x+k);
  k := 10;
  r
)
will return a function that adds 10 to its argument rather than 5. This is because the extra dictionary is created only when the context in which the function was defined ends, which is when the function f returns. This is consistent with how you would expect the function r to work inside the function f according to the rules of scope of variables in GEL. Only those variables are added to the extra dictionary that are in the context that just ended and no longer exists. Variables used in the function that are in still valid contexts will work as usual, using the current value of the variable. The only difference is with global variables and functions. All identifiers that referenced global variables at time of the function definition are not added to the private dictionary. This is to avoid much unnecessary work when returning functions and would rarely be a problem. For example, suppose that you delete the "k=5" from the function f, and at the top level you define k to be say 5. Then when you run f, the function r will not put k into the private dictionary because it was global (toplevel) at the time of definition of r.

Sometimes it is better to have more control over how variables are copied into the private dictionary. Since version 1.0.7, you can specify which variables are copied into the private dictionary by putting extra square brackets after the arguments with the list of variables to be copied separated by commas. If you do this, then variables are copied into the private dictionary at time of the function definition, and the private dictionary is not touched afterwards. For example

function f() = (
  k := 5;
  function r(x) [k] = (x+k);
  k := 10;
  r
)
will return a function that when called will add 5 to its argument. The local copy of k was created when the function was defined.

When you want the function to not have any private dictionary when 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.