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. Note that the last semicolon inside a scope is optional.
// Scopes evaluate to the last expression they contain.
{ 1; 2; 3};[00000000] 3// They are expressions.
echo({1; 2; 3});[00000000] *** 3
Scopes can be nested. Variables can be redefined in sub-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)
return a
else
return 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(rate)
{
var charges = 100;
function computeSalary(hours)
{
// Here 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 write to captured variables, as shown below.
var a = 0;[00000000] 0
var b = 0;[00000000] 0
function add(n)
{
// x and y are updated by closure
a += n;
b += n;
void
} | {};
add(25);
add(25);
add(1);
a;[00000000] 51
b;[00000000] 51
Closure can be really powerful tools in some situation, and they are even more useful when combined with functional programing, as described in Chapter 10.