2.2. Imperative Extensions

This section gives examples for the imperative extensions. These are not related to relational data and can be seen as extensions of the Java programming language on their own. They remain within the imperative programming paradigm.

2.2.1. Expression Lists and the Comma Operator

The C programming language defines the comma operator which can be used to construct expression lists like (t = a*sin(x), sqrt(1 - t*t)). It evaluates its left and right subexpressions and returns the value of the latter. The comma operator is not defined in the Java programming language, but reintroduced in the XL programming language by the specification of expression lists (Section 16.12, “Expression Lists”).

Expression lists are useful if a function (t -> sqrt(1 - t*t) in the example) is to be applied to an expression (a*sin(x)) and if this function does not exist as a named function. A temporary variable (t) is needed, which can be declared explicitly in the expression list, (double t = a*Math.sin(x), Math.sqrt(1 - t*t)), or implicitly as in ($ = a*Math.sin(x), Math.sqrt(1 - $*$)), using the special identifier $.

2.2.2. Instance Scope Expressions

Instance scope expressions (Section 16.7, “Instance Scope Expressions”) are similar to the with-statement of the Basic and Pascal programming languages. They can be used when several consecutive expressions access fields or methods of a common instance as in

c.getBounds().setLocation(0, 0);
c.getBounds().width = 100;
c.getBounds().height = 50;

Here, c.getBounds() is the common instance. Using an instance scope expression, the example is reduced to

c.getBounds().(setLocation(0, 0), width = 100, height = 500)

Thus, an instance scope expression includes the instance (the expression on the left hand side of .) in the scope of the right hand side. The main purpose is the use within initialization of objects as in

Cylinder a = new Cylinder().(setRadius(2), setColor(0xff0000), setAxis(1, 0, 0));

In addition, if a local variable with identifier $ has not yet been defined in the current scope, then the instance can be addressed within the right expression using this special identifier.

2.2.3. Operator Overloading

The XL programming language provides a mechanism for operator overloading as it is known from other programming languages, e.g., C++. This allows programmers to add new meanings to operators like +. For example, consider a class Complex which is declared as follows:

class Complex {
    double real;
    double imag;

    Complex (double real, double imag) {
        this.real = real;
        this.imag = imag;
    }

    Complex operator+ (Complex b) {
        return new Complex(this.real + b.real, this.imag + b.imag);
    }
}

The method declaration operator+ declares an operator method (Section 11.2.2, “Operator Method Declarations”). This method gives the operator + a meaning in the context of Complex numbers in the following way: An expression a + b, where a and b are Complex numbers, is translated into the method invocation expression a.operator+(b).

2.2.4. Generators, Aggregate Methods, and Filter Methods

A very useful new feature of the XL programming language are generators (Section 16.1, “Generator Expressions and Sequential Evaluation”). They yield multiple results in succession. As an example, consider the range expression 1 : 10 (Section 16.11, “Range Operator”): This is a very simple generator which yields the values 1 up to 10, one after another. Such a generator may be used as the iterator of a for-statement:

for (int i : (1 : 10)) { /* do something */ }

Or it may be used as an argument to aggregate methods (Section 16.2.3, “Aggregate Method Invocations”) which perform computations on the set of yielded values and return a single value:

int p = prod(1 : 10);
int s = sum(2 * (1 : 4));

This example makes use of the aggregate methods prod and sum which are declared in the class de.grogra.xl.lang.Operators. Aggregate methods can also be used for arrays as in

int[] a = {1, 2, 3, 4, 5};
int s = sum(a);

Generators and aggregate methods are especially useful in the context of queries (Section 2.1.2, “Queries”). For example, the following expression computes the length of all instances of class F whose diameter exceeds a threshold t:

float len = sum((* f:F, (f.diameter > t) *).length);

New generators can be declared using generator methods (Section 11.2.1, “Generator Method Declarations”). They are indicated in the method header by an asterisk between the result type and the name, and they may yield multiple values via the yield-statement:

long* fibonacci(int n) {
  long a = 1, b = 1;
  while (--n >= 0) {
    yield a;
    long t = a; a = b; b += t;
  }
}

Now the sum of the first 10 Fibonacci numbers can be computed by the expression sum(fibonacci(10)), or these numbers can be written to the console by

System.out.println(fibonacci(10));

The generator method could be implemented without termination condition. In principle, then it computes the Fibonacci numbers ad infinitum:

long* fibonacci() {
  long a = 1, b = 1;
  while (true) {
    yield a;
    long t = a; a = b; b += t;
  }
}

Now the termination has to be implemented externally, as in

int n = 10;
for (long f : fibonacci()) {
  System.out.println(f);
  if (--n < 0) {
    break;
  }
}

Such a termination can also be implemented by filter methods (Section 16.2.4, “Filter Method Invocations”) which filter values of generator expressions. For example, the filter method slice in the class de.grogra.xl.lang.Operators filters a slice of values:

System.out.println(slice(fibonacci(), 10, 20));

This writes the Fibonacci numbers from 10 (inclusive) to 20 (exclusive) to the console.