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.
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 $
.
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.
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)
.
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.