urbiscript support functional programming through first class functions and lambda expressions.
urbiscript has first class functions, i.e., functions are regular values, just like integers or strings. They can be stored in variables, passed as arguments to other functions, and so forth. For instance, you don’t need to write function object.f(){/* ... */} to insert a function in an object, you can simply use setSlot.
var o = Object.clone()|;
// Here we can use f as any regular value.
o.setSlotValue("m1", function () { echo("Hello") })|;
// This is strictly equivalent/
var o.m2 = function () { echo("Hello") }|;
o.m1();
[00000000] *** Hello
o.m2();
[00000000] *** Hello
This enables to write powerful pieces of code, like functions that take function as argument. For instance, consider the all function: given a list and a function, it applies the function to each element of the list, and returns whether all calls returned true. This enables to check very simply if all elements in a list verify a predicate.
function all(list, predicate)
{
for (var elt : list)
if (!predicate(elt))
return false;
return true;
}|;
// Check if all elements in a list are positive.
function positive(x) { x >= 0 }|;
all([1, 2, 3], getSlotValue("positive"));
[00000000] true
all([1, 2, -3], getSlotValue("positive"));
[00000000] false
It turns out that all already exists: instead of all(list, predicate), use list.all(predicate), see RangeIterable.all.
Another nice feature is the ability to write lambda functions, which are anonymous functions. You can create a functional value as an expression, without naming it, with the syntax shown below.
// Create an anonymous function
function (x) {x + 1}|;
// This enable to easily pass function
// to our "all" function:
[1, 2, 3].all(function (x) { x > 0});
[00000000] true
In fact, the function construct we saw earlier is only a shorthand for a variable assignment.
// This ...
function obj.f (/*...*/) {/*...*/};
// ... is actually a shorthand for:
const var obj.f = function (/*...*/) {/* ... */};
Most popular programming languages use strict arguments evaluation: arguments are evaluated before functions are called. Other languages use lazy evaluation: argument are evaluated by the function only when needed. In urbiscript, evaluation is strict by default, but you can ask a function not to evaluate its arguments, and do it by hand. This works by not specifying formal arguments. The function is provided with a call object that enables you to evaluate arguments.
// Note the lack of formal arguments specification
function first
{
// Evaluate only the first argument.
call.evalArgAt(0);
}|;
first(echo("first"), echo("second"));
[00000000] *** first
function reverse
{
call.evalArgAt(1);
call.evalArgAt(0);
}|;
reverse(echo("first"), echo("second"));
[00000000] *** second
[00000000] *** first
A good example are logic operators. Although C++ is a strict language, it uses a few logic operators. For instance, the logical and (&&) does not evaluate its right operand if the left operand is false (the result will be false anyway).
urbiscript logic operator mimic this behavior. The listing below shows how one can implement such a behavior.