This section presents advanced uses of functions and scoping, as well as their combo: lexical closures, which prove to be a very powerful tool.
Contrary to other languages from the C family, scopes are expressions: they can be used where values are expected, just as 1 + 1 or "foo". They evaluate to the value of their last expression, or void if they are empty. The following listing illustrates the use of scopes as expressions. The last semicolon inside a scope is optional.
// Scopes evaluate to the value of their last expression.
{ 1; 2; 3; };
[00000000] 3
// They are expressions.
echo({1; 2; 3});
[00000000] *** 3
Scopes can be nested. Variables can be redefined in nested scopes. In this case, the inner variables hide the outer ones, as illustrated below.
var x = 0; // Define the outer x.
[00000000] 0
{
var x = 1; // Define an inner x.
x = 2; // These refer to
echo(x); // the inner x
};
[00000000] *** 2
x; // This is the outer x again.
[00000000] 0
{
x = 3; // This is still the outer x.
echo(x);
};
[00000000] *** 3
x;
[00000000] 3
Functions can be defined anywhere local variables can — that is, about anywhere. These functions’ visibility are limited to the scope they’re defined in, like variables. This enables for instance to write local helper functions like max2 in the example below.
function max3(a, b, c) // Max of three values
{
function max2(a, b)
{
if (a > b)
a
else
b
};
max2(a, max2(b, c));
}|;
A closure is the capture by a function of a variable external to this function. urbiscript supports lexical closure: functions can refer to outer local variables, as long as they are visible (in scope) from where the function is defined.
function printSalaries(var rate)
{
var charges = 100;
function computeSalary(var hours)
{
// rate and charges are captured from the environment by closure.
rate * hours - charges
};
echo("Alice’s salary is " + computeSalary(35));
echo("Bob’s salary is " + computeSalary(30));
}|;
printSalaries(15);
[00000000] *** Alice’s salary is 425
[00000000] *** Bob’s salary is 350
Closures can also change captured variables, as shown below.
var a = 0;
[00000000] 0
var b = 0;
[00000000] 0
function add(n)
{
// a and b are updated by closure.
a += n;
b += n;
{}
}|;
add(25);
add(25);
add(1);
a;
[00000000] 51
b;
[00000000] 51
Closure can be really powerful tools in some situations; they are even more useful when combined with functional programming, as described in Listing 9.