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
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.