Next

Prev

Prev-tail

Tail

Up

Chapter 10
Advanced Functions and Scoping

This section presents advanced uses of functions and scoping, as well as their combo: lexical closures, which prove to be a very powerful tool.

 10.1 Scopes as expressions
 10.2 Advanced scoping
 10.3 Local functions
 10.4 Lexical closures

10.1 Scopes as expressions

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  

10.2 Advanced scoping

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  

10.3 Local functions

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)); 
} | {};  

10.4 Lexical closures

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("Alices salary is " + computeSalary(35)); 
  echo("Bobs salary is " + computeSalary(30)); 
} | {}; 
printSalaries(15); 
[00000000] *** Alices salary is 425 
[00000000] *** Bobs 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 12.